DOCUMENTACIÓN API

Guía completa para el uso de la API de nuestro servicio

🔓 Autenticación y Seguridad (Spring Security)

Autenticación (AuthController)
POST /api/v1/auth/register – Registrar nuevo usuario (público)
POST /api/v1/auth/register
Content-Type: application/json

Body (JSON):

{
  "nombre": "Juan Pérez",
  "email": "juan@correo.com",
  "password": "SecurePwd!23"
}

  • Descripción: Registra un usuario con rol CLIENTE por defecto. No requiere JWT.
  • Validaciones (DTO UsuarioRegisterDTO):
    • nombre: no puede estar vacío.
    • email: debe tener formato válido y no estar ya registrado.
    • password: mínimo 8 caracteres (u otras reglas definidas).
  • Respuesta (201 Created):
    {
      "id": 15,
      "nombre": "Juan Pérez",
      "email": "juan@correo.com",
      "rol": "CLIENTE",
      "fechaCreado": "2025-06-02"
    }
POST /api/v1/auth/login – Obtener JWT (público)
POST /api/v1/auth/login
Content-Type: application/json

Body (JSON):

{
  "email": "juan@correo.com",
  "password": "SecurePwd!23"
}

  • Descripción: Autentica las credenciales y devuelve un token JWT.
  • Respuesta (200 OK):
    {
      "token": "eyJhbGciOiJIUzI1NiJ9…"
    }
Alérgenos (AlergenoController)
GET /api/v1/alergenos – Listar todos los alérgenos (público)
GET /api/v1/alergenos

  • Descripción: Devuelve una lista de AlergenoResponseDTO con todos los alérgenos registrados.
  • No requiere token.
  • Ejemplo de respuesta (200 OK):
    [
      { "id": 1, "nombre": "GLUTEN" },
      { "id": 2, "nombre": "LACTEOS" },
      { "id": 4, "nombre": "HUEVO" },
      { "id": 5, "nombre": "PESCADO" }
    ]
POST /api/v1/alergenos?nombre=xxxx – Crear nuevo alérgeno (solo ADMIN)
POST /api/v1/alergenos?nombre=PESCADO
Authorization: Bearer <token_admin>

  • Roles permitidos: ADMIN
  • Descripción: Crea un nuevo alérgeno, pasando el parámetro nombre por query string.
  • Ejemplo de respuesta (201 Created):
    {
      "id": 9,
      "nombre": "PESCADO"
    }
DELETE /api/v1/alergenos/{id} – Eliminar alérgeno (solo ADMIN)
DELETE /api/v1/alergenos/5
Authorization: Bearer <token_admin>

  • Roles permitidos: ADMIN
  • Descripción: Elimina el alérgeno cuyo id = 5.
  • Respuesta: 204 No Content.
Categorías (CategoriaController)
GET /api/v1/categorias – Listar todas las categorías (público)
GET /api/v1/categorias

  • Descripción: Devuelve lista de CategoriaResponseDTO con todas las categorías de platos.
  • No requiere token.
  • Ejemplo de respuesta (200 OK):
    [
      { "id": 1, "nombreCategoria": "Entrantes" },
      { "id": 2, "nombreCategoria": "Pescados" },
      { "id": 3, "nombreCategoria": "Postres" }
    ]
POST /api/v1/categorias – Crear categoría (solo ADMIN)
POST /api/v1/categorias
Authorization: Bearer <token_admin>
Content-Type: application/json

Body (JSON):

{
  "nombreCategoria": "Bebidas"
}

  • Roles permitidos: ADMIN
  • Validaciones (CategoriaRequestDTO):
    • nombreCategoria: no vacío.
  • Respuesta (201 Created):
    {
      "id": 4,
      "nombreCategoria": "Bebidas"
    }
PUT /api/v1/categorias/{id} – Actualizar categoría (solo ADMIN)
PUT /api/v1/categorias/3
Authorization: Bearer <token_admin>
Content-Type: application/json

Body (JSON):

{
  "nombreCategoria": "Postres Caseros"
}

  • Roles permitidos: ADMIN
  • Respuesta (200 OK):
    {
      "id": 3,
      "nombreCategoria": "Postres Caseros"
    }
DELETE /api/v1/categorias/{id} – Eliminar categoría (solo ADMIN)
DELETE /api/v1/categorias/3
Authorization: Bearer <token_admin>

  • Roles permitidos: ADMIN
  • Descripción: Elimina la categoría con id = 3 (si hay platos asociados, se debe manejar cascada o fallará por integridad).
  • Respuesta: 204 No Content.
Proveedores (ProveedorController)
POST /api/v1/proveedores – Crear proveedor (solo ADMIN)
POST /api/v1/proveedores
Authorization: Bearer <token_admin>
Content-Type: application/json

Body (JSON):

{
  "nombreProveedor": "Proveedor C",
  "contacto": "+34-600-123-456"
}

  • Roles permitidos: ADMIN
  • Descripción: Crea un nuevo proveedor.
  • Respuesta (201 Created):
    {
      "id": 3,
      "nombreProveedor": "Proveedor C",
      "contacto": "+34-600-123-456"
    }
GET /api/v1/proveedores – Listar proveedores (solo ADMIN)
GET /api/v1/proveedores
Authorization: Bearer <token_admin>

  • Roles permitidos: ADMIN
  • Descripción: Devuelve lista de todos los proveedores.
  • Ejemplo de respuesta (200 OK):
    [
      { "id": 1, "nombreProveedor": "Proveedor A", "contacto": "123-456-789" },
      { "id": 2, "nombreProveedor": "Proveedor B", "contacto": "987-654-321" }
    ]
GET /api/v1/proveedores/{id} – Obtener proveedor por ID (solo ADMIN)
GET /api/v1/proveedores/1
Authorization: Bearer <token_admin>

  • Roles permitidos: ADMIN
  • Descripción: Devuelve los datos del proveedor cuyo id = 1.
  • Ejemplo de respuesta (200 OK):
    {
      "id": 1,
      "nombreProveedor": "Proveedor A",
      "contacto": "123-456-789"
    }
PUT /api/v1/proveedores/{id} – Actualizar proveedor (solo ADMIN)
PUT /api/v1/proveedores/2
Authorization: Bearer <token_admin>
Content-Type: application/json

Body (JSON):

{
  "nombreProveedor": "Proveedor B Actualizado",
  "contacto": "+34-700-987-654"
}

  • Roles permitidos: ADMIN
  • Descripción: Actualiza los datos del proveedor con id = 2.
  • Ejemplo de respuesta (200 OK):
    {
      "id": 2,
      "nombreProveedor": "Proveedor B Actualizado",
      "contacto": "+34-700-987-654"
    }
DELETE /api/v1/proveedores/{id} – Eliminar proveedor (solo ADMIN)
DELETE /api/v1/proveedores/2
Authorization: Bearer <token_admin>

  • Roles permitidos: ADMIN
  • Descripción: Elimina el proveedor con id = 2 (si hay ingredientes o platos asociados, primero deben eliminarse).
  • Respuesta: 204 No Content.
Ingredientes (IngredientesController)
GET /api/v1/ingredientes?page=&size= – Listar ingredientes (público)
GET /api/v1/ingredientes?page=0&size=20

  • Descripción: Devuelve un paginado de IngredienteResponseDTO. Cada entrada incluye:
    • id
    • nombre
    • categoria
    • proveedor
    • cantidadEnvase
    • unidadEnvase
    • precioEnvase
    • precioUnitario
    • unidad
    • alergenos (lista de { id, nombre })
  • No requiere token.
  • Ejemplo de respuesta (200 OK):
    {
      "content": [
        {
          "id":             10,
          "nombre":         "Aceite de Oliva",
          "categoria":      "Aceites",
          "proveedor":      "Proveedor C",
          "cantidadEnvase": 2000.00,
          "unidadEnvase":   "ml",
          "precioEnvase":   6.00,
          "precioUnitario": 0.0030,
          "unidad":         "ml",
          "alergenos":      []
        },
        {
          "id":             8,
          "nombre":         "Azúcar",
          "categoria":      "Condimentos",
          "proveedor":      "Proveedor C",
          "cantidadEnvase": 1000.00,
          "unidadEnvase":   "gramos",
          "precioEnvase":   1.00,
          "precioUnitario": 0.0010,
          "unidad":         "gramos",
          "alergenos":      []
        },
        {
          "id":             4,
          "nombre":         "Bacalao Desalado",
          "categoria":      "Pescados",
          "proveedor":      "Proveedor C",
          "cantidadEnvase": 1000.00,
          "unidadEnvase":   "gramos",
          "precioEnvase":   5.00,
          "precioUnitario": 0.0050,
          "unidad":         "gramos",
          "alergenos": [
            { "id": 5, "nombre": "PESCADO" }
          ]
        }
        // … etc …
      ],
      "pageable":       { … },
      "totalPages":     1,
      "totalElements":  10,
      "last":           true,
      "size":           20,
      "number":         0,
      "sort":           { "sorted": true, "unsorted": false, "empty": false },
      "numberOfElements": 10,
      "first":           true,
      "empty":           false
    }
GET /api/v1/ingredientes/{id} – Obtener ingrediente por ID (público)
GET /api/v1/ingredientes/4

  • Descripción: Devuelve IngredienteResponseDTO para id = 4.
  • No requiere token.
  • Ejemplo de respuesta (200 OK):
    {
      "id": 4,
      "nombre": "Bacalao Desalado",
      "categoria": "Pescados",
      "proveedor": "Proveedor C",
      "cantidadEnvase": 1000.0,
      "unidadEnvase": "gramos",
      "precioEnvase": 5.0,
      "precioUnitario": 0.005,
      "unidad": "gramos",
      "alergenos": [{ "id": 5, "nombre": "PESCADO" }]
    }
GET /api/v1/ingredientes/por-proveedor/{id}?page=&size= – Listar por proveedor (público)
GET /api/v1/ingredientes/por-proveedor/3?page=0&size=20

  • Descripción: Devuelve Page<IngredienteResponseDTO> de los ingredientes cuyo proveedor_id = 3.
  • No requiere token.
  • Ejemplo de respuesta (200 OK):
    {
      "content": [
        {
          "id":             7,
          "nombre":         "Lechuga",
          "categoria":      "Verduras",
          "proveedor":      "Proveedor A",
          "cantidadEnvase": 1000.00,
          "unidadEnvase":   "gramos",
          "precioEnvase":   1.00,
          "precioUnitario": 0.0010,
          "unidad":         "gramos",
          "alergenos":      []
        },
        {
          "id":             5,
          "nombre":         "Patata",
          "categoria":      "Verduras",
          "proveedor":      "Proveedor A",
          "cantidadEnvase": 5000.00,
          "unidadEnvase":   "gramos",
          "precioEnvase":   2.50,
          "precioUnitario": 0.0005,
          "unidad":         "gramos",
          "alergenos":      []
        }
      ],
      "pageable":       { … },
      "totalPages":     1,
      "totalElements":  2,
      "last":           true,
      "size":           20,
      "number":         0,
      "sort":           { "sorted": true, "unsorted": false, "empty": false },
      "numberOfElements": 2,
      "first":           true,
      "empty":           false
    }
GET /api/v1/ingredientes/{id}/alergenos – Obtener alérgenos de un ingrediente (público)
GET /api/v1/ingredientes/4/alergenos

  • Descripción: Recupera la lista de AlergenoResponseDTO asociados al ingrediente con id = 4.
  • No requiere token.
  • Ejemplo de respuesta (200 OK):
    [{ "id": 5, "nombre": "PESCADO" }]
POST /api/v1/ingredientes/{ingredienteId}/alergenos/{alergenoId} – Añadir alérgeno a ingrediente (solo ADMIN)
POST /api/v1/ingredientes/4/alergenos/2
Authorization: Bearer <token_admin>

  • Roles permitidos: ADMIN
  • Descripción: Asocia el alérgeno alergenoId = 2 al ingrediente ingredienteId = 4.
  • Ejemplo de respuesta (200 OK):
    {
      "id":       4,
      "nombre":   "Bacalao Seco",
      "categoria": "Pescados",
      "proveedor": "Proveedor C",
      "cantidadEnvase": 1200.0,
      "unidadEnvase":   "gramos",
      "precioEnvase":   6.0,
      "precioUnitario": 0.005,
      "unidad":         "gramos",
      "alergenos": [
        { "id": 2, "nombre": "LACTEOS" },
        { "id": 5, "nombre": "PESCADO" }
      ]
    }
PUT /api/v1/ingredientes/{id} – Actualizar ingrediente (solo ADMIN)
PUT /api/v1/ingredientes/4
Authorization: Bearer <token_admin>
Content-Type: application/json

Body (JSON):

{
  "nombre":         "Bacalao Seco",
  "categoria":      "Pescados",
  "proveedorId":    3,
  "cantidadEnvase": 1200.0,
  "unidadEnvase":   "gramos",
  "precioEnvase":   6.0,
  "unidad":         "gramos",
  "alergenosIds":   [5]
}

  • Roles permitidos: ADMIN
  • Flujo Interno:
    1. Valida que el Ingrediente con id = 4 exista (o lanza 404).
    2. Valida que Proveedor con proveedorId exista.
    3. Actualiza campos básicos y vuelve a calcular precioUnitario = precioEnvase / cantidadEnvase.
    4. Borra todas las filas antiguas de ingrediente_alergeno para dicho ingredienteId.
    5. Por cada idAler en alergenosIds, valida que el alérgeno exista y crea una fila en ingrediente_alergeno.
    6. Devuelve el IngredienteResponseDTO completo, con la lista actualizada de alérgenos.
  • Ejemplo de respuesta (200 OK):
    {
      "id":       4,
      "nombre":   "Bacalao Seco",
      "categoria": "Pescados",
      "proveedor": "Proveedor C",
      "cantidadEnvase": 1200.0,
      "unidadEnvase":   "gramos",
      "precioEnvase":   6.0,
      "precioUnitario": 0.005,
      "unidad":         "gramos",
      "alergenos": [{ "id": 5, "nombre": "PESCADO" }]
    }
DELETE /api/v1/ingredientes/{id} – Eliminar ingrediente (solo ADMIN)
DELETE /api/v1/ingredientes/4
Authorization: Bearer <token_admin>

  • Roles permitidos: ADMIN
  • Descripción: Elimina el ingrediente con id = 4; las filas en la tabla ingrediente_alergeno se borran en cascada.
  • Respuesta: 204 No Content.
Platos (PlatoController)
GET /api/v1/platos?page=&size= – Listar todos los platos (público)
GET /api/v1/platos?page=0&size=20

Descripción: Devuelve un Page<PlatoListDTO> paginado, ordenado por nombrePlato. Cada PlatoListDTO incluye:

  • id
  • nombrePlato
  • descripcion
  • categoria (objeto con { id, nombreCategoria })
  • precio
  • stock
  • imagenUrl (si existe)

No requiere token.

Ejemplo de respuesta (200 OK):

                  {
                            "content": [
                            
                              {
                                "id":           4,
                                "nombrePlato":  "Tarta de Queso",
                                "descripcion":  "Tarta cremosa de queso",
                                "categoria":    { "id": 3, "nombreCategoria": "Postres" },
                                "precio":       4.50,
                                "stock":        20,
                                "imagenUrl":    "http://…/tarta.jpg"
                              }
                              // … etc …
                            ],
                            "pageable":       { … },
                            "totalPages":     1,
                            "totalElements":  2,
                            "last":           true,
                            "size":           20,
                            "number":         0,
                            "sort":           { "sorted": true, "unsorted": false, "empty": false },
                            "numberOfElements": 2,
                            "first":           true,
                            "empty":           false
                }
              
GET /api/v1/platos/{id} – Obtener plato por ID (público)
GET /api/v1/platos/2

  • Descripción: Devuelve un PlatoResponseDTO con todos los detalles del plato id = 2.
  • No requiere token.
  • Ejemplo de respuesta (200 OK):
    {
      "id": 2,
      "nombrePlato": "Paella Valenciana",
      "descripcion": "Paella con mariscos y pollo",
      "categoria": { "id": 1, "nombreCategoria": "Entrantes" },
      "precio": 12.0,
      "stock": 10,
      "imagenUrl": "http://…/paella.jpg"
    }
GET /api/v1/platos/categoria?nombre=&page=&size= – Buscar platos por categoría (público)
GET /api/v1/platos/categoria?nombre=Pescados&page=0&size=20

  • Descripción: Busca platos cuyo nombre de categoría contenga “Pescados” (o exacto), devuelve Page<PlatoListDTO>.
  • No requiere token.
  • Ejemplo de respuesta (200 OK):
    {
      "content": [
        {
          "id":           4,
          "nombrePlato":  "Bacalao al Horno",
          "descripcion":  "Bacalao con patatas y cebolla",
          "categoria":    { "id": 2, "nombreCategoria": "Pescados" },
          "precio":       10.00,
          "stock":        15,
          "imagenUrl":    null
        }
        // … etc …
      ],
      "pageable":       { … },
      "totalPages":     1,
      "totalElements":  1,
      "last":           true,
      "size":           20,
      "number":         0,
      "sort":           { "sorted": true, "unsorted": false, "empty": false },
      "numberOfElements": 1,
      "first":           true,
      "empty":           false
    }
GET /api/v1/platos/buscar?nombre=&page=&size= – Buscar platos por nombre (público)
GET /api/v1/platos/buscar?nombre=Paella&page=0&size=20

  • Descripción: Busca platos cuyo nombrePlato contenga “Paella”. Devuelve Page<PlatoListDTO>.
  • No requiere token.
  • Ejemplo de respuesta (200 OK):
    {
      "content": [
        {
          "id":           2,
          "nombrePlato":  "Paella Valenciana",
          "descripcion":  "Paella con mariscos y pollo",
          "categoria":    { "id": 1, "nombreCategoria": "Entrantes" },
          "precio":       12.00,
          "stock":        10,
          "imagenUrl":    "http://…/paella.jpg"
        }
      ],
      "pageable":       { … },
      "totalPages":     1,
      "totalElements":  1,
      "last":           true,
      "size":           20,
      "number":         0,
      "sort":           { "sorted": true, "unsorted": false, "empty": false },
      "numberOfElements": 1,
      "first":           true,
      "empty":           false
    }
GET /api/v1/platos/{id}/ingredientes – Obtener lista de ingredientes de un plato (público)
GET /api/v1/platos/2/ingredientes

  • Descripción: Devuelve un List<IngredienteDetalleDTO> con detalle de ingredientes requeridos para preparar el plato id = 2.
  • No requiere token.
  • Ejemplo de respuesta (200 OK):
    [
      {
        "ingredienteId":     4,
        "nombre":            "Bacalao Desalado",
        "cantidadRequerida": 200,
        "unidad":            "g"
      },
      {
        "ingredienteId":     8,
        "nombre":            "Azúcar",
        "cantidadRequerida": 50,
        "unidad":            "g"
      }
      // … etc …
    ]
GET /api/v1/platos/{id}/alergenos – Obtener lista deduplicada de alérgenos de un plato (público)
GET /api/v1/platos/2/alergenos

  • Descripción: Devuelve un List<AlergenoResponseDTO> con los alérgenos que aparecen en cualquiera de los ingredientes del plato id = 2.
  • No requiere token.
  • Ejemplo de respuesta (200 OK):
    [
      { "id": 1, "nombre": "GLUTEN" },
      { "id": 4, "nombre": "HUEVO" }
    ]
POST /api/v1/platos – Crear nuevo plato (solo ADMIN)
POST /api/v1/platos
Authorization: Bearer <token_admin>
Content-Type: application/json

Body (JSON):

{
  "nombrePlato":     "Nueva receta",
  "descripcion":     "Descripción del plato",
  "categoriaId":     2,
  "precio":          8.5,
  "stock":           30,
  "imagenUrl":       "http://…/nuevaplatillo.jpg"
}

  • Roles permitidos: ADMIN
  • Validaciones (PlatoRequestDTO):
    • nombrePlato, descripcion: no vacíos.
    • categoriaId: debe existir.
    • precio > 0.
    • stock ≥ 0.
  • Ejemplo de respuesta (201 Created):
    {
      "id":           10,
      "nombrePlato":  "Nueva receta",
      "descripcion":  "Descripción del plato",
      "categoria":    { "id": 2, "nombreCategoria": "Pescados" },
      "precio":       8.5,
      "stock":        30,
      "imagenUrl":    "http://…/nuevaplatillo.jpg"
    }
PUT /api/v1/platos/{id} – Actualizar plato (solo ADMIN)
PUT /api/v1/platos/2
Authorization: Bearer <token_admin>
Content-Type: application/json

Body (JSON):

{
  "nombrePlato":  "Paella Mixta",
  "descripcion":  "Paella con mariscos, pollo y conejo",
  "categoriaId":  1,
  "precio":       13.0,
  "stock":        8,
  "imagenUrl":    "http://…/paellamixta.jpg"
}

  • Roles permitidos: ADMIN
  • Ejemplo de respuesta (200 OK):
    {
      "id":           2,
      "nombrePlato":  "Paella Mixta",
      "descripcion":  "Paella con mariscos, pollo y conejo",
      "categoria":    { "id": 1, "nombreCategoria": "Entrantes" },
      "precio":       13.0,
      "stock":        8,
      "imagenUrl":    "http://…/paellamixta.jpg"
    }
DELETE /api/v1/platos/{id} – Eliminar plato (solo ADMIN)
DELETE /api/v1/platos/2
Authorization: Bearer <token_admin>

  • Roles permitidos: ADMIN
  • Descripción: Elimina el plato con id = 2 (si hay registros en otras tablas que lo referencien, gestionarla vía cascada o fallará).
  • Respuesta: 204 No Content.
Estados de Pedido (EstadoController)
GET /api/v1/estados – Listar todos los estados (CLIENTE o ADMIN)
GET /api/v1/estados
Authorization: Bearer <token_cliente_o_admin>

  • Roles permitidos: CLIENTE, ADMIN
  • Descripción: Devuelve lista de EstadoResponseDTO (p.ej.: Pendiente, Enviado, Entregado, Cancelado).
  • Ejemplo de respuesta (200 OK):
    [
      { "id": 1, "nombre": "Pendiente" },
      { "id": 2, "nombre": "Enviado" },
      { "id": 3, "nombre": "Entregado" },
      { "id": 4, "nombre": "Cancelado" }
    ]
POST /api/v1/estados – Crear estado (solo ADMIN)
POST /api/v1/estados
Authorization: Bearer <token_admin>
Content-Type: application/json

Body (JSON):

{
  "nombre": "Preparando"
}

  • Roles permitidos: ADMIN
  • Ejemplo de respuesta (201 Created):
    {
      "id": 5,
      "nombre": "Preparando"
    }
DELETE /api/v1/estados/{id} – Eliminar estado (solo ADMIN)
DELETE /api/v1/estados/4
Authorization: Bearer <token_admin>

  • Roles permitidos: ADMIN
  • Descripción: Elimina el estado con id = 4. Si hay pedidos asociados, arroja error de integridad.
  • Respuesta: 204 No Content.
Pedidos (PedidoController)
POST /api/v1/pedidos – Crear nuevo pedido (CLIENTE o ADMIN)
POST /api/v1/pedidos
Authorization: Bearer <token_cliente_o_admin>
Content-Type: application/json

Body (JSON):

{
  "fechaEntrega":     "2025-06-05",
  "direccionEntrega": "Av Siempre Viva 742",
  "detalles": [
    {
      "platoId":       2,
      "cantidadPlato": 5,
      "descuento":     0.0
    },
    {
      "platoId":       4,
      "cantidadPlato": 2,
      "descuento":     0.0
    }
  ]
}

  • Roles permitidos: CLIENTE, ADMIN
  • Descripción (flujo):
    1. Se obtiene el usuario autenticado del JWT y el estado “Pendiente”.
    2. Se crea la cabecera Pedido con total = 0.
    3. Por cada DetallesPedidoDTO:
      • Valida existencia de Plato y stock suficiente.
      • Resta el stock y guarda el cambio en plato.stock.
      • Calcula subtotalLínea = precioUnitario × cantidadPlato – descuento.
      • Suma subtotalLínea al acumulado total.
      • Inserta registro en detalles_pedido.
    4. Actualiza el campo total del pedido en la base de datos.
    5. Recarga el pedido con sus detalles (pedidoService.crearPedido(…) devuelve el PedidoResponseDTO).
    6. Genera PDF con iText y obtiene byte[] pdfBytes.
    7. Envía correo al cliente con PDF adjunto.
    8. Devuelve PedidoResponseDTO completo.
  • Validaciones (PedidoRequestDTO):
    • fechaEntrega ≥ fecha actual.
    • direccionEntrega: no vacía.
    • detalles ≥ 1 elemento.
    • Cada detalle: cantidadPlato ≥ 1, descuento ≥ 0.
    • Stock insuficiente → 400 Bad Request.
  • Ejemplo de respuesta (201 Created):
    {
      "id":               6,
      "estado":           "Pendiente",
      "nombreCliente":    "EmiOP",
      "emailCliente":     "eordunap@hotmail.com",
      "fechaCreado":      "2025-06-02",
      "fechaEntrega":     "2025-06-05",
      "direccionEntrega": "Av Siempre Viva 742",
      "total":            69.0,
      "fechaActualizacion":"2025-06-02T21:24:32.2077845",
      "detalles": [
        { "platoId": 2, "cantidadPlato": 5, "descuento": 0.0 },
        { "platoId": 4, "cantidadPlato": 2, "descuento": 0.0 }
      ]
    }
GET /api/v1/pedidos/usuario – Listar los pedidos del usuario autenticado (CLIENTE o ADMIN)
GET /api/v1/pedidos/usuario
Authorization: Bearer <token_cliente_o_admin>

  • Roles permitidos: CLIENTE, ADMIN
  • Descripción: Devuelve un List<PedidoListDTO> (sin detalle de líneas) con los pedidos del usuario autenticado.
  • Ejemplo de respuesta (200 OK):
    [
      {
        "id":           4,
        "nombreCliente":"EmiOP",
        "estado":       "Entregado",
        "fechaEntrega": "2025-05-28",
        "totalPlatos":  3,
        "total":        37.5
      },
      {
        "id":           6,
        "nombreCliente":"EmiOP",
        "estado":       "Pendiente",
        "fechaEntrega": "2025-06-05",
        "totalPlatos":  7,
        "total":        69.0
      }
    ]
GET /api/v1/pedidos?page=&size=&estadoId= – Listar todos los pedidos (solo ADMIN)
GET /api/v1/pedidos?page=0&size=10&estadoId=2
Authorization: Bearer <token_admin>

  • Roles permitidos: ADMIN
  • Descripción: Devuelve un paginado (Page<PedidoListDTO>) de todos los pedidos. Opcionalmente filtra por estadoId.
  • Ejemplo de respuesta (200 OK):
    {
      "content": [
        {
          "id":            8,
          "nombreCliente":"María López",
          "estado":        "Enviado",
          "fechaEntrega":  "2025-06-03",
          "totalPlatos":   4,
          "total":         45.00
        }
        // … etc …
      ],
      "pageable":       { … },
      "totalPages":     5,
      "totalElements":  45,
      "last":           false,
      "size":           10,
      "number":         0,
      "sort":           { "sorted": true, "unsorted": false, "empty": false },
      "numberOfElements": 10,
      "first":           true,
      "empty":           false
    }
GET /api/v1/pedidos/{id} – Obtener pedido por ID (CLIENTE o ADMIN)
GET /api/v1/pedidos/6
Authorization: Bearer <token_cliente_o_admin>

  • Roles permitidos:
    • ADMIN (puede ver cualquier pedido).
    • CLIENTE (solo si el pedido le pertenece).
  • Descripción: Devuelve PedidoResponseDTO completo, con detalle de líneas y todos los campos.
  • Ejemplo de respuesta (200 OK):
    {
      "id":               6,
      "estado":           "Pendiente",
      "nombreCliente":    "EmiOP",
      "emailCliente":     "eordunap@hotmail.com",
      "fechaCreado":      "2025-06-02",
      "fechaEntrega":     "2025-06-05",
      "direccionEntrega": "Av Siempre Viva 742",
      "total":            69.0,
      "fechaActualizacion":"2025-06-02T21:24:32.2077845",
      "detalles": [
        { "platoId": 2, "cantidadPlato": 5, "descuento": 0.0 },
        { "platoId": 4, "cantidadPlato": 2, "descuento": 0.0 }
      ]
    }
DELETE /api/v1/pedidos/{id} – Cancelar o eliminar pedido (CLIENTE o ADMIN)
DELETE /api/v1/pedidos/6
Authorization: Bearer <token_cliente_o_admin>

  • Si el usuario autenticado es CLIENTE:
    • Solo puede cancelar su propio pedido si estado = “Pendiente”.
    • Se actualiza estado a “Cancelado” y se restaura stock de cada plato.
    • Devuelve 204 No Content.
  • Si el usuario autenticado es ADMIN:
    • Restaura stock de todas las líneas del pedido.
    • Borra físicamente el pedido y sus detalles (orphanRemoval = true).
    • Devuelve 204 No Content.
PUT /api/v1/pedidos/{id}/estado/{estadoId} – Cambiar estado de pedido (solo ADMIN)
PUT /api/v1/pedidos/6/estado/3
Authorization: Bearer <token_admin>

  • Roles permitidos: ADMIN
  • Descripción: Actualiza el estado del pedido con id = 6 al estadoId = 3 (“Entregado”). Internamente registra un log de auditoría.
  • Ejemplo de respuesta (200 OK):
    {
      "id":               6,
      "estado":           "Entregado",
      "nombreCliente":    "EmiOP",
      "emailCliente":     "eordunap@hotmail.com",
      "fechaCreado":      "2025-06-02",
      "fechaEntrega":     "2025-06-05",
      "direccionEntrega": "Av Siempre Viva 742",
      "total":            69.0,
      "fechaActualizacion":"2025-06-03T11:12:45.123",
      "detalles": [
        { "platoId": 2, "cantidadPlato": 5, "descuento": 0.0 },
        { "platoId": 4, "cantidadPlato": 2, "descuento": 0.0 }
      ]
    }
Facturas (FacturaController)
GET /api/v1/facturas/{pedidoId} – Descargar factura en PDF (CLIENTE o ADMIN)
GET /api/v1/facturas/6
Authorization: Bearer <token_cliente_o_admin>

  • Roles permitidos:
    • ADMIN (puede descargar factura de cualquier pedido).
    • CLIENTE (solo si el pedido le pertenece).
  • Descripción:
    1. Busca el pedido pedidoId en BD. Si no existe, devuelve 404 Not Found.
    2. Genera un PDF en memoria con iText. El PDF contiene:
      • Título (centrado, “Factura – Tecocinamos”).
      • Tabla con datos generales: ID de factura, fecha pedido, fecha entrega, nombre cliente, email, teléfono, dirección de entrega.
      • Tabla con líneas de pedido (Producto, Cant., Precio Unit., Descuento, Total Línea). Si no hay líneas, muestra “No hay productos en este pedido”.
      • Total final (alineado a la derecha).
      • Pie de página (centrado, “Gracias por confiar en Tecocinamos. ¡Esperamos verte pronto!”).
    3. Devuelve el PDF con:
      Content-Type: application/pdf
      Content-Disposition: inline; filename="factura_pedido_6.pdf"
    4. Si el cliente lo abre en navegador, se muestra incrustado; si lo descarga, recibe el archivo factura_pedido_6.pdf.
  • Respuesta (200 OK): El cuerpo es el binario del PDF. No hay JSON.
Estadísticas (EstadisticaController)

Nota: La clase EstadisticaController está anotada con @PreAuthorize("hasRole('ADMIN')") a nivel de controlador, por lo que todos sus endpoints requieren ADMIN.

GET /api/v1/estadisticas/top-platos?topN={n} – Top N platos vendidos (solo ADMIN)
GET /api/v1/estadisticas/top-platos?topN=5
Authorization: Bearer <token_admin>

  • Roles permitidos: ADMIN
  • Descripción: Devuelve un List<Map<String,Object>> donde cada elemento es { "nombrePlato": "...", "cantidadVendida": 123 }, ordenados de mayor a menor ventas.
  • Ejemplo de respuesta (200 OK):
    [
      { "nombrePlato": "Paella Valenciana", "cantidadVendida": 150 },
      { "nombrePlato": "Tarta de Queso",    "cantidadVendida": 120 }
      // … hasta topN elementos …
    ]
GET /api/v1/estadisticas/ingresos?desde=&hasta= – Ingresos por período (solo ADMIN)
GET /api/v1/estadisticas/ingresos?desde=2025-01-01&hasta=2025-06-01
Authorization: Bearer <token_admin>

  • Roles permitidos: ADMIN
  • Descripción: Calcula el total de ingresos sumando pedido.total para pedidos cuya fechaCreado esté en el rango [desde, hasta].
    • Si la fecha no tiene formato YYYY-MM-DD, devuelve 400 Bad Request con { "error": "Formato de fecha inválido. Use YYYY-MM-DD" }.
  • Ejemplo de respuesta (200 OK):
    {
      "ingresos": 2450.75,
      "desde":    "2025-01-01",
      "hasta":    "2025-06-01"
    }
GET /api/v1/estadisticas/pedidos-por-estado – Pedidos agrupados por estado (solo ADMIN)
GET /api/v1/estadisticas/pedidos-por-estado
Authorization: Bearer <token_admin>

  • Roles permitidos: ADMIN
  • Descripción: Devuelve un List<Map<String,Object>> donde cada elemento es { "estado": "Pendiente", "cantidad": 25 } para cada estado existente.
  • Ejemplo de respuesta (200 OK):
    [
      { "estado": "Pendiente",  "cantidad": 5   },
      { "estado": "Enviado",    "cantidad": 10  },
      { "estado": "Entregado",  "cantidad": 100 },
      { "estado": "Cancelado",  "cantidad": 2   }
    ]
Log Auditoría (LogAuditoriaController)

Nota: El controller está anotado con @PreAuthorize("hasRole('ADMIN')"), por lo que solo ADMIN puede acceder.

GET /api/v1/logs – Listar todos los logs de auditoría (solo ADMIN)
GET /api/v1/logs
Authorization: Bearer <token_admin>

  • Roles permitidos: ADMIN
  • Descripción: Devuelve un List<LogAuditoriaDTO>, donde cada DTO incluye:
    • id
    • entidad (p.ej., “Pedido”, “Usuario”)
    • campo (p.ej., “estado”, “email”)
    • valorAnterior
    • valorNuevo
    • operacion (p.ej., “Cambio de estado”, “Edición de usuario”)
    • timestamp
    • usuario (email del admin que ejecutó la operación)
  • Ejemplo de respuesta (200 OK):
    [
      {
        "id": 15,
        "entidad":          "Pedido",
        "campo":            "estado",
        "valorAnterior":    "Pendiente",
        "valorNuevo":       "Entregado",
        "operacion":        "Cambio de estado",
        "timestamp":        "2025-06-01T18:22:10",
        "usuario":          "admin@tecocinamos.com"
      },
      {
        "id": 14,
        "entidad":          "Usuario",
        "campo":            "email",
        "valorAnterior":    "pepito@correo.com",
        "valorNuevo":       "pepitojp@correo.com",
        "operacion":        "Edición de usuario",
        "timestamp":        "2025-05-30T11:05:45",
        "usuario":          "admin@tecocinamos.com"
      }
      // … etc …
    ]
Usuarios Públicos y Perfil (UsuarioController)
GET /api/v1/users/me – Perfil del usuario autenticado (CLIENTE o ADMIN)
GET /api/v1/users/me
Authorization: Bearer <token_cliente_o_admin>

  • Roles permitidos: CLIENTE, ADMIN
  • Descripción: Devuelve UsuarioResponseDTO con los datos del usuario autenticado (nombre, email, rol, fechaCreado, teléfono, etc.).
  • Ejemplo de respuesta (200 OK):
    {
      "id":           5,
      "nombre":       "EmiOP",
      "email":        "eordunap@hotmail.com",
      "rol":          "CLIENTE",
      "fechaCreado":  "2025-05-30",
      "telefono":     "+34-123-456-789"
    }
PUT /api/v1/users/password – Cambiar contraseña (CLIENTE o ADMIN)
PUT /api/v1/users/password
Authorization: Bearer <token_cliente_o_admin>
Content-Type: application/json

Body (JSON) CambiarPasswordDTO:

{
  "passwordActual": "OldPass123!",
  "nuevoPassword":  "NewPass456!"
}

  • Roles permitidos: CLIENTE, ADMIN
  • Descripción:
    • Verifica que passwordActual coincida con la contraseña almacenada (BCrypt).
    • Si coincide, actualiza al nuevoPassword (hash BCrypt).
    • Devuelve 204 No Content.
  • Respuesta: 204 No Content.
PUT /api/v1/users/perfil – Actualizar nombre y teléfono (CLIENTE o ADMIN)
PUT /api/v1/users/perfil
Authorization: Bearer <token_cliente_o_admin>
Content-Type: application/json

Body (JSON) ActualizarPerfilDTO:

{
  "nombre":   "Emilio OP",
  "telefono": "+34-987-654-321"
}

  • Roles permitidos: CLIENTE, ADMIN
  • Descripción: Actualiza los datos del perfil (nombre y teléfono) del usuario autenticado.
  • Ejemplo de respuesta (200 OK):
    {
      "id":          5,
      "nombre":      "Emilio OP",
      "email":       "eordunap@hotmail.com",
      "rol":         "CLIENTE",
      "fechaCreado": "2025-05-30",
      "telefono":    "+34-987-654-321"
    }
GET /api/v1/users/profile/{nombre} – Ver perfil público de un usuario (público)
GET /api/v1/users/profile/EmiOP

  • Descripción: Busca el usuario por nombre (username/nombre único) y devuelve UsuarioPublicDTO, que suele incluir solo campos públicos (p.ej., nombre, telefono). No revela email ni datos sensibles.
  • No requiere token.
  • Ejemplo de respuesta (200 OK):
    {
      "nombre":   "EmiOP",
      "telefono": "+34-123-456-789"
    }
GET /api/v1/users/search?name={nombre} – Buscar usuarios por nombre (solo ADMIN)
GET /api/v1/users/search?name=Juan
Authorization: Bearer <token_admin>

  • Roles permitidos: ADMIN
  • Descripción: Devuelve un List<UsuarioResponseDTO> de usuarios cuyo nombre coincida parcial o totalmente con el parámetro name.
  • Ejemplo de respuesta (200 OK):
    [
      {
        "id":          12,
        "nombre":      "Juan Pérez",
        "email":       "juan@correo.com",
        "rol":         "CLIENTE",
        "fechaCreado": "2025-05-20",
        "telefono":    "+34-600-111-222"
      }
      // … otros usuarios …
    ]
GET /api/v1/users – Listar todos los usuarios (solo ADMIN)
GET /api/v1/users
Authorization: Bearer <token_admin>

  • Roles permitidos: ADMIN
  • Descripción: Devuelve lista de UsuarioResponseDTO con todos los usuarios.
  • Ejemplo de respuesta (200 OK):
    [
      {
        "id":           5,
        "nombre":       "EmiOP",
        "email":        "eordunap@hotmail.com",
        "rol":          "CLIENTE",
        "fechaCreado":  "2025-05-30",
        "telefono":     "+34-123-456-789"
      },
      {
        "id":           12,
        "nombre":       "Juan Pérez",
        "email":        "juan@correo.com",
        "rol":          "CLIENTE",
        "fechaCreado":  "2025-05-20",
        "telefono":     "+34-600-111-222"
      }
      // … etc …
    ]
GET /api/v1/users/{id} – Obtener usuario por ID (solo ADMIN)
GET /api/v1/users/12
Authorization: Bearer <token_admin>

  • Roles permitidos: ADMIN
  • Descripción: Devuelve UsuarioResponseDTO con todos los datos del usuario id = 12.
  • Ejemplo de respuesta (200 OK):
    {
      "id":           12,
      "nombre":       "Juan Pérez",
      "email":        "juan@correo.com",
      "rol":          "CLIENTE",
      "fechaCreado":  "2025-05-20",
      "telefono":     "+34-600-111-222"
    }
DELETE /api/v1/users/{id} – Eliminar usuario (solo ADMIN)
DELETE /api/v1/users/12
Authorization: Bearer <token_admin>

  • Roles permitidos: ADMIN
  • Descripción: “Elimina lógicamente” al usuario con id = 12 (soft delete o marca eliminado = true).
  • Respuesta: 204 No Content.
PUT /api/v1/users/{id}/rol/{rolId} – Asignar rol a usuario (solo ADMIN)
PUT /api/v1/users/12/rol/1
Authorization: Bearer <token_admin>

  • Roles permitidos: ADMIN
  • Descripción: Asigna el rol de rolId = 1 (ADMIN) al usuario con id = 12.
  • Ejemplo de respuesta (200 OK):
    {
      "id":           12,
      "nombre":       "Juan Pérez",
      "email":        "juan@correo.com",
      "rol":          "ADMIN",
      "fechaCreado":  "2025-05-20",
      "telefono":     "+34-600-111-222"
    }