Commit ab607cc1 by Rubén Ramírez

fix: [Recursos]: Actualizada la manera de trabajar con las fotos para…

fix: [Recursos]: Actualizada la manera de trabajar con las fotos para almacenarlas también en la BBDD
parent 3a98e108
...@@ -6,7 +6,6 @@ import jakarta.persistence.EntityManager; ...@@ -6,7 +6,6 @@ import jakarta.persistence.EntityManager;
import jakarta.persistence.PersistenceContext; import jakarta.persistence.PersistenceContext;
import org.springframework.stereotype.Repository; import org.springframework.stereotype.Repository;
import org.springframework.transaction.annotation.Transactional; import org.springframework.transaction.annotation.Transactional;
import java.time.LocalDate; import java.time.LocalDate;
import java.util.List; import java.util.List;
...@@ -28,17 +27,16 @@ public class RepositorioRecurso { ...@@ -28,17 +27,16 @@ public class RepositorioRecurso {
@Transactional(readOnly = true) @Transactional(readOnly = true)
public List<Recurso> buscarPorTitulo(String titulo) { public List<Recurso> buscarPorTitulo(String titulo) {
return em.createQuery( return em.createQuery(
"SELECT r FROM Recurso r WHERE LOWER(r.titulo) LIKE LOWER(:titulo)", "SELECT r FROM Recurso r WHERE LOWER(r.titulo) LIKE LOWER(:titulo)",
Recurso.class) Recurso.class)
.setParameter("titulo", "%" + titulo + "%") .setParameter("titulo", "%" + titulo + "%")
.getResultList(); .getResultList();
} }
@Transactional(readOnly = true) @Transactional(readOnly = true)
public List<Recurso> buscarPorAutor(String autor) { public List<Recurso> buscarPorAutor(String autor) {
return em.createQuery( return em.createQuery(
"SELECT r FROM Recurso r WHERE LOWER(r.autor) LIKE LOWER(:autor)", "SELECT r FROM Recurso r WHERE LOWER(r.autor) LIKE LOWER(:autor)",
Recurso.class) Recurso.class)
.setParameter("autor", "%" + autor + "%") .setParameter("autor", "%" + autor + "%")
.getResultList(); .getResultList();
...@@ -47,7 +45,7 @@ public class RepositorioRecurso { ...@@ -47,7 +45,7 @@ public class RepositorioRecurso {
@Transactional(readOnly = true) @Transactional(readOnly = true)
public List<Recurso> buscarPorGenero(Genero genero) { public List<Recurso> buscarPorGenero(Genero genero) {
return em.createQuery( return em.createQuery(
"SELECT r FROM Recurso r JOIN r.generos g WHERE g = :genero", "SELECT r FROM Recurso r JOIN r.generos g WHERE g = :genero",
Recurso.class) Recurso.class)
.setParameter("genero", genero) .setParameter("genero", genero)
.getResultList(); .getResultList();
...@@ -56,7 +54,7 @@ public class RepositorioRecurso { ...@@ -56,7 +54,7 @@ public class RepositorioRecurso {
@Transactional(readOnly = true) @Transactional(readOnly = true)
public List<Recurso> buscarPorRangoFechas(LocalDate fechaInicio, LocalDate fechaFin) { public List<Recurso> buscarPorRangoFechas(LocalDate fechaInicio, LocalDate fechaFin) {
return em.createQuery( return em.createQuery(
"SELECT r FROM Recurso r WHERE r.fechaPublicacion BETWEEN :inicio AND :fin", "SELECT r FROM Recurso r WHERE r.fechaPublicacion BETWEEN :inicio AND :fin",
Recurso.class) Recurso.class)
.setParameter("inicio", fechaInicio) .setParameter("inicio", fechaInicio)
.setParameter("fin", fechaFin) .setParameter("fin", fechaFin)
......
package com.ujaen.tfg.mangaffinity.rest; package com.ujaen.tfg.mangaffinity.rest;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.ujaen.tfg.mangaffinity.entidades.BibliotecaPersonal; import com.ujaen.tfg.mangaffinity.entidades.BibliotecaPersonal;
import com.ujaen.tfg.mangaffinity.entidades.BibliotecaPersonalRecurso; import com.ujaen.tfg.mangaffinity.entidades.BibliotecaPersonalRecurso;
import com.ujaen.tfg.mangaffinity.entidades.Categoria; import com.ujaen.tfg.mangaffinity.entidades.Categoria;
import com.ujaen.tfg.mangaffinity.entidades.Recurso; import com.ujaen.tfg.mangaffinity.entidades.Recurso;
import com.ujaen.tfg.mangaffinity.excepciones.RecursoNoExiste;
import com.ujaen.tfg.mangaffinity.rest.DTO.DTORecursoEnBiblioteca; import com.ujaen.tfg.mangaffinity.rest.DTO.DTORecursoEnBiblioteca;
import com.ujaen.tfg.mangaffinity.rest.DTO.Mapper;
import com.ujaen.tfg.mangaffinity.servicios.ServicioBibliotecaPersonal; import com.ujaen.tfg.mangaffinity.servicios.ServicioBibliotecaPersonal;
import com.ujaen.tfg.mangaffinity.servicios.ServicioRecursos; import com.ujaen.tfg.mangaffinity.servicios.ServicioRecursos;
import com.ujaen.tfg.mangaffinity.servicios.ServicioUsuarios; import com.ujaen.tfg.mangaffinity.servicios.ServicioUsuarios;
...@@ -15,14 +12,14 @@ import org.springframework.beans.factory.annotation.Autowired; ...@@ -15,14 +12,14 @@ import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus; import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity; import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*; import org.springframework.web.bind.annotation.*;
import java.util.Base64;
import java.util.List; import java.util.List;
import java.util.stream.Collectors; import java.util.stream.Collectors;
@RestController @RestController
@RequestMapping("/biblioteca") @RequestMapping("/biblioteca")
public class BibliotecaPersonalController { public class BibliotecaPersonalController {
private static final Logger logger = LoggerFactory.getLogger(BibliotecaPersonalController.class);
@Autowired @Autowired
private ServicioBibliotecaPersonal servicioBibliotecaPersonal; private ServicioBibliotecaPersonal servicioBibliotecaPersonal;
...@@ -32,9 +29,6 @@ public class BibliotecaPersonalController { ...@@ -32,9 +29,6 @@ public class BibliotecaPersonalController {
@Autowired @Autowired
private ServicioUsuarios servicioUsuarios; private ServicioUsuarios servicioUsuarios;
@Autowired
private Mapper mapper;
@PostMapping("/{usuarioId}/recursos/{recursoId}/categoria") @PostMapping("/{usuarioId}/recursos/{recursoId}/categoria")
public ResponseEntity<String> anadirRecursoBiblioteca( public ResponseEntity<String> anadirRecursoBiblioteca(
@PathVariable Long usuarioId, @PathVariable Long usuarioId,
...@@ -51,7 +45,6 @@ public class BibliotecaPersonalController { ...@@ -51,7 +45,6 @@ public class BibliotecaPersonalController {
return ResponseEntity.status(HttpStatus.NOT_FOUND).build(); return ResponseEntity.status(HttpStatus.NOT_FOUND).build();
} }
boolean yaExiste = servicioBibliotecaPersonal.listarPorCategoria(biblioteca.getId(), dtoRecursoEnBiblioteca.getCategoria()) boolean yaExiste = servicioBibliotecaPersonal.listarPorCategoria(biblioteca.getId(), dtoRecursoEnBiblioteca.getCategoria())
.stream() .stream()
.anyMatch(bpr -> bpr.getRecurso().getId().equals(recursoId)); .anyMatch(bpr -> bpr.getRecurso().getId().equals(recursoId));
...@@ -67,11 +60,9 @@ public class BibliotecaPersonalController { ...@@ -67,11 +60,9 @@ public class BibliotecaPersonalController {
} }
} }
@GetMapping("/{usuarioId}/recursos/categoria/{categoria}") @GetMapping("/{usuarioId}/recursos/categoria/{categoria}")
public ResponseEntity<List<DTORecursoEnBiblioteca>> listarRecursosPorCategoria( public ResponseEntity<List<DTORecursoEnBiblioteca>> listarRecursosPorCategoria(
@PathVariable Long usuarioId, @PathVariable String categoria) { // 🔥 Ahora es String @PathVariable Long usuarioId, @PathVariable String categoria) {
try { try {
BibliotecaPersonal biblioteca = servicioUsuarios.obtenerBibliotecaDeUsuario(usuarioId); BibliotecaPersonal biblioteca = servicioUsuarios.obtenerBibliotecaDeUsuario(usuarioId);
if (biblioteca == null) { if (biblioteca == null) {
...@@ -82,17 +73,23 @@ public class BibliotecaPersonalController { ...@@ -82,17 +73,23 @@ public class BibliotecaPersonalController {
try { try {
categoriaEnum = Categoria.valueOf(categoria.toUpperCase()); categoriaEnum = Categoria.valueOf(categoria.toUpperCase());
} catch (IllegalArgumentException e) { } catch (IllegalArgumentException e) {
return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(null); // ⚠ Si la categoría no es válida return ResponseEntity.status(HttpStatus.BAD_REQUEST).build();
} }
List<BibliotecaPersonalRecurso> recursos = servicioBibliotecaPersonal.listarPorCategoria(biblioteca.getId(), categoriaEnum); List<BibliotecaPersonalRecurso> recursos = servicioBibliotecaPersonal.listarPorCategoria(biblioteca.getId(), categoriaEnum);
List<DTORecursoEnBiblioteca> dtoRecursos = recursos.stream() List<DTORecursoEnBiblioteca> dtoRecursos = recursos.stream()
.map(bpr -> new DTORecursoEnBiblioteca( .map(bpr -> {
bpr.getRecurso().getId(), String fotoBase64 = (bpr.getRecurso().getFoto() != null && bpr.getRecurso().getFoto().length > 0)
bpr.getRecurso().getTitulo(), ? "data:image/jpeg;base64," + Base64.getEncoder().encodeToString(bpr.getRecurso().getFoto())
bpr.getCategoria(), : null;
bpr.getRecurso().getFotoUrl() // 🔥 Aquí añadimos la imagen
)) return new DTORecursoEnBiblioteca(
bpr.getRecurso().getId(),
bpr.getRecurso().getTitulo(),
bpr.getCategoria(),
fotoBase64
);
})
.collect(Collectors.toList()); .collect(Collectors.toList());
...@@ -102,22 +99,6 @@ public class BibliotecaPersonalController { ...@@ -102,22 +99,6 @@ public class BibliotecaPersonalController {
} }
} }
@PutMapping("/{usuarioId}/recursos/{recursoId}/categoria")
public ResponseEntity<String> cambiarCategoria(
@PathVariable Long usuarioId,
@PathVariable Long recursoId,
@RequestBody DTORecursoEnBiblioteca dtoRecurso) {
try {
BibliotecaPersonal biblioteca = servicioUsuarios.obtenerBibliotecaDeUsuario(usuarioId);
if (biblioteca == null) return ResponseEntity.status(HttpStatus.NOT_FOUND).build();
servicioBibliotecaPersonal.cambiarCategoriaRecurso(biblioteca.getId(), recursoId, dtoRecurso.getCategoria());
return ResponseEntity.status(HttpStatus.OK).build();
} catch (Exception e) {
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).build();
}
}
@DeleteMapping("/{usuarioId}/recursos/{recursoId}") @DeleteMapping("/{usuarioId}/recursos/{recursoId}")
public ResponseEntity<String> eliminarRecursoDeBiblioteca( public ResponseEntity<String> eliminarRecursoDeBiblioteca(
@PathVariable Long usuarioId, @PathVariable Long usuarioId,
...@@ -127,7 +108,7 @@ public class BibliotecaPersonalController { ...@@ -127,7 +108,7 @@ public class BibliotecaPersonalController {
if (biblioteca == null) return ResponseEntity.status(HttpStatus.NOT_FOUND).build(); if (biblioteca == null) return ResponseEntity.status(HttpStatus.NOT_FOUND).build();
servicioBibliotecaPersonal.eliminarRecursoDeBiblioteca(biblioteca.getId(), recursoId); servicioBibliotecaPersonal.eliminarRecursoDeBiblioteca(biblioteca.getId(), recursoId);
return ResponseEntity.status(HttpStatus.NO_CONTENT).build(); // Código 204 para eliminación exitosa return ResponseEntity.status(HttpStatus.NO_CONTENT).build();
} catch (Exception e) { } catch (Exception e) {
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).build(); return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).build();
} }
...@@ -137,38 +118,44 @@ public class BibliotecaPersonalController { ...@@ -137,38 +118,44 @@ public class BibliotecaPersonalController {
public ResponseEntity<DTORecursoEnBiblioteca> obtenerRecursoEnBiblioteca( public ResponseEntity<DTORecursoEnBiblioteca> obtenerRecursoEnBiblioteca(
@PathVariable Long usuarioId, @PathVariable Long recursoId) { @PathVariable Long usuarioId, @PathVariable Long recursoId) {
try { try {
logger.info("Recibiendo solicitud para obtener recurso con ID: {} para usuario con ID: {}", recursoId, usuarioId);
// Obtener la biblioteca personal del usuario
BibliotecaPersonal biblioteca = servicioUsuarios.obtenerBibliotecaDeUsuario(usuarioId); BibliotecaPersonal biblioteca = servicioUsuarios.obtenerBibliotecaDeUsuario(usuarioId);
if (biblioteca == null) { if (biblioteca == null) {
logger.warn("No se encontró la biblioteca para el usuario con ID: {}", usuarioId); return ResponseEntity.status(HttpStatus.NOT_FOUND).body(null);
return ResponseEntity.status(HttpStatus.NOT_FOUND).body(null); // Si no existe la biblioteca del usuario
} }
// Buscar el recurso dentro de la biblioteca del usuario
BibliotecaPersonalRecurso bibliotecaPersonalRecurso = servicioBibliotecaPersonal.buscarRecursoEnBiblioteca(biblioteca.getId(), recursoId); BibliotecaPersonalRecurso bibliotecaPersonalRecurso = servicioBibliotecaPersonal.buscarRecursoEnBiblioteca(biblioteca.getId(), recursoId);
if (bibliotecaPersonalRecurso == null) { if (bibliotecaPersonalRecurso == null) {
logger.warn("No se encontró el recurso con ID: {} en la biblioteca del usuario con ID: {}", recursoId, usuarioId); return ResponseEntity.status(HttpStatus.NOT_FOUND).body(null);
return ResponseEntity.status(HttpStatus.NOT_FOUND).body(null); // Si no se encuentra el recurso en la biblioteca
} }
// Mapear el recurso encontrado al DTO para retornar
DTORecursoEnBiblioteca dtoRecursoEnBiblioteca = new DTORecursoEnBiblioteca( DTORecursoEnBiblioteca dtoRecursoEnBiblioteca = new DTORecursoEnBiblioteca(
bibliotecaPersonalRecurso.getRecurso().getId(), bibliotecaPersonalRecurso.getRecurso().getId(),
bibliotecaPersonalRecurso.getRecurso().getTitulo(), bibliotecaPersonalRecurso.getRecurso().getTitulo(),
bibliotecaPersonalRecurso.getCategoria(), bibliotecaPersonalRecurso.getCategoria(),
bibliotecaPersonalRecurso.getRecurso().getFotoUrl() (bibliotecaPersonalRecurso.getRecurso().getFoto() != null)
? Base64.getEncoder().encodeToString(bibliotecaPersonalRecurso.getRecurso().getFoto())
: null
); );
logger.info("Recurso con ID: {} encontrado para el usuario con ID: {}", recursoId, usuarioId);
return ResponseEntity.ok(dtoRecursoEnBiblioteca); return ResponseEntity.ok(dtoRecursoEnBiblioteca);
} catch (Exception e) { } catch (Exception e) {
logger.error("Error inesperado al obtener el recurso con ID: {} para el usuario con ID: {}", recursoId, usuarioId, e); return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).build();
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).build(); // Si ocurre algún error inesperado
} }
} }
@PutMapping("/{usuarioId}/recursos/{recursoId}/categoria")
public ResponseEntity<String> cambiarCategoria(
@PathVariable Long usuarioId,
@PathVariable Long recursoId,
@RequestBody DTORecursoEnBiblioteca dtoRecurso) {
try {
BibliotecaPersonal biblioteca = servicioUsuarios.obtenerBibliotecaDeUsuario(usuarioId);
if (biblioteca == null) return ResponseEntity.status(HttpStatus.NOT_FOUND).build();
servicioBibliotecaPersonal.cambiarCategoriaRecurso(biblioteca.getId(), recursoId, dtoRecurso.getCategoria());
} return ResponseEntity.status(HttpStatus.OK).build();
} catch (Exception e) {
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).build();
}
}
}
package com.ujaen.tfg.mangaffinity.rest.DTO; package com.ujaen.tfg.mangaffinity.rest.DTO;
import com.ujaen.tfg.mangaffinity.entidades.Genero; import com.ujaen.tfg.mangaffinity.entidades.Genero;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.NotNull;
import lombok.AllArgsConstructor; import lombok.AllArgsConstructor;
import lombok.Getter; import lombok.Getter;
import lombok.NoArgsConstructor; import lombok.NoArgsConstructor;
import lombok.Setter; import lombok.Setter;
import java.time.LocalDate; import java.time.LocalDate;
import java.util.HashSet;
import java.util.Set; import java.util.Set;
@Getter @Getter
...@@ -17,19 +15,11 @@ import java.util.Set; ...@@ -17,19 +15,11 @@ import java.util.Set;
@AllArgsConstructor @AllArgsConstructor
public class DTORecurso { public class DTORecurso {
private Long id; private Long id;
@NotBlank
private String titulo; private String titulo;
private String descripcion; private String descripcion;
@NotNull
private LocalDate fechaPublicacion; private LocalDate fechaPublicacion;
@NotBlank
private String autor; private String autor;
private String fotoBase64;
private String fotoUrl; private Set<Genero> generos;
private Set<Genero> generos = new HashSet<>();
} }
...@@ -11,16 +11,8 @@ import lombok.Setter; ...@@ -11,16 +11,8 @@ import lombok.Setter;
@NoArgsConstructor @NoArgsConstructor
@AllArgsConstructor @AllArgsConstructor
public class DTORecursoEnBiblioteca { public class DTORecursoEnBiblioteca {
private Long usuarioId;
private Long recursoId; private Long recursoId;
private String titulo; private String titulo;
private Categoria categoria; private Categoria categoria;
private String fotoUrl; private String fotoBase64;
public DTORecursoEnBiblioteca(Long recursoId, String titulo, Categoria categoria, String fotoUrl) {
this.recursoId = recursoId;
this.titulo = titulo;
this.categoria = categoria;
this.fotoUrl = fotoUrl;
}
} }
...@@ -3,53 +3,26 @@ package com.ujaen.tfg.mangaffinity.rest; ...@@ -3,53 +3,26 @@ package com.ujaen.tfg.mangaffinity.rest;
import com.ujaen.tfg.mangaffinity.entidades.*; import com.ujaen.tfg.mangaffinity.entidades.*;
import com.ujaen.tfg.mangaffinity.excepciones.CapituloNoExiste; import com.ujaen.tfg.mangaffinity.excepciones.CapituloNoExiste;
import com.ujaen.tfg.mangaffinity.excepciones.RecursoNoExiste; import com.ujaen.tfg.mangaffinity.excepciones.RecursoNoExiste;
import com.ujaen.tfg.mangaffinity.rest.DTO.DTOCapitulo; import com.ujaen.tfg.mangaffinity.rest.DTO.*;
import com.ujaen.tfg.mangaffinity.rest.DTO.DTORecurso;
import com.ujaen.tfg.mangaffinity.rest.DTO.Mapper;
import com.ujaen.tfg.mangaffinity.servicios.ServicioRecursos; import com.ujaen.tfg.mangaffinity.servicios.ServicioRecursos;
import jakarta.validation.Valid;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus; import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity; import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*; import org.springframework.web.bind.annotation.*;
import java.time.LocalDate; import java.time.LocalDate;
import java.util.Collections; import java.util.*;
import java.util.List; import org.springframework.web.multipart.MultipartFile;import com.ujaen.tfg.mangaffinity.entidades.Recurso;
import java.util.Map;
import java.io.File;import org.springframework.web.multipart.MultipartFile;import com.ujaen.tfg.mangaffinity.entidades.Recurso;
import com.ujaen.tfg.mangaffinity.rest.DTO.DTORecurso; import com.ujaen.tfg.mangaffinity.rest.DTO.DTORecurso;
import com.ujaen.tfg.mangaffinity.rest.DTO.Mapper; import com.ujaen.tfg.mangaffinity.rest.DTO.Mapper;
import com.ujaen.tfg.mangaffinity.servicios.ServicioRecursos;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;
import java.io.File;
import java.io.IOException; import java.io.IOException;
import java.nio.file.*;
import java.time.LocalDate;
import java.util.UUID;import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.StandardCopyOption;
@RestController @RestController
@RequestMapping("/recursos") @RequestMapping("/recursos")
public class RecursosController { public class RecursosController {
@Autowired @Autowired
private ServicioRecursos servicioRecursos; private ServicioRecursos servicioRecursos;
@Autowired @Autowired
private Mapper mapper; private Mapper mapper;
...@@ -57,50 +30,31 @@ public class RecursosController { ...@@ -57,50 +30,31 @@ public class RecursosController {
public ResponseEntity<String> crearRecurso( public ResponseEntity<String> crearRecurso(
@RequestPart("recurso") DTORecurso recursoDTO, @RequestPart("recurso") DTORecurso recursoDTO,
@RequestPart(value = "foto", required = false) MultipartFile foto) { @RequestPart(value = "foto", required = false) MultipartFile foto) {
try { try {
String fileName = null; byte[] fotoBytes = null;
// Verificar si se adjuntó una imagen y si es un tipo válido
if (foto != null && !foto.isEmpty()) { if (foto != null && !foto.isEmpty()) {
String mimeType = foto.getContentType(); String mimeType = foto.getContentType();
if (mimeType == null || (!mimeType.equals("image/jpeg") && !mimeType.equals("image/png"))) { if (mimeType == null || (!mimeType.equals("image/jpeg") && !mimeType.equals("image/png"))) {
return ResponseEntity.status(HttpStatus.BAD_REQUEST).body("Solo se permiten imágenes JPEG o PNG."); return ResponseEntity.status(HttpStatus.BAD_REQUEST).body("Solo se permiten imágenes JPEG o PNG.");
} }
fotoBytes = foto.getBytes();
// Obtener el nombre original y limpiar el nombre del archivo
String originalFileName = foto.getOriginalFilename();
if (originalFileName != null) {
// Reemplazar espacios y caracteres especiales en el nombre
fileName = originalFileName.replaceAll("\\s+", "_").replaceAll("[^a-zA-Z0-9._-]", "");
} else {
fileName = "imagen_predeterminada.png";
}
// Ruta de almacenamiento
Path uploadsPath = Paths.get("uploads/");
if (!Files.exists(uploadsPath)) {
Files.createDirectories(uploadsPath);
}
// Guardar la imagen en la carpeta uploads/
Path filePath = uploadsPath.resolve(fileName);
Files.copy(foto.getInputStream(), filePath, StandardCopyOption.REPLACE_EXISTING);
} }
// Crear el recurso con solo el nombre del archivo en la BD
Recurso nuevoRecurso = new Recurso( Recurso nuevoRecurso = new Recurso(
recursoDTO.getTitulo(), recursoDTO.getTitulo(),
recursoDTO.getDescripcion(), recursoDTO.getDescripcion(),
recursoDTO.getFechaPublicacion(), recursoDTO.getFechaPublicacion(),
recursoDTO.getAutor(), recursoDTO.getAutor(),
fileName // Guardamos solo el nombre del archivo fotoBytes
); );
nuevoRecurso.setGeneros(recursoDTO.getGeneros()); nuevoRecurso.setGeneros(recursoDTO.getGeneros());
servicioRecursos.crearRecurso(nuevoRecurso); servicioRecursos.crearRecurso(nuevoRecurso);
return ResponseEntity.status(HttpStatus.CREATED).build(); return ResponseEntity.status(HttpStatus.CREATED).build();
} catch (IOException e) { } catch (IOException e) {
...@@ -108,9 +62,6 @@ public class RecursosController { ...@@ -108,9 +62,6 @@ public class RecursosController {
} }
} }
@GetMapping("/titulo/{titulo}") @GetMapping("/titulo/{titulo}")
public ResponseEntity<List<DTORecurso>> buscarPorTitulo(@PathVariable String titulo) { public ResponseEntity<List<DTORecurso>> buscarPorTitulo(@PathVariable String titulo) {
try { try {
...@@ -125,7 +76,6 @@ public class RecursosController { ...@@ -125,7 +76,6 @@ public class RecursosController {
} }
} }
@GetMapping("/{id}/capitulos/tipos") @GetMapping("/{id}/capitulos/tipos")
public ResponseEntity<Map<String, List<DTOCapitulo>>> obtenerCapitulosPorTipo(@PathVariable Long id) { public ResponseEntity<Map<String, List<DTOCapitulo>>> obtenerCapitulosPorTipo(@PathVariable Long id) {
try { try {
...@@ -142,7 +92,7 @@ public class RecursosController { ...@@ -142,7 +92,7 @@ public class RecursosController {
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).build(); return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).build();
} }
} }
@GetMapping("/genero/{genero}") @GetMapping("/genero/{genero}")
public ResponseEntity<List<DTORecurso>> buscarPorGenero(@PathVariable Genero genero) { public ResponseEntity<List<DTORecurso>> buscarPorGenero(@PathVariable Genero genero) {
try { try {
...@@ -165,7 +115,6 @@ public class RecursosController { ...@@ -165,7 +115,6 @@ public class RecursosController {
} }
} }
@GetMapping("/autor/{autor}") @GetMapping("/autor/{autor}")
public ResponseEntity<List<DTORecurso>> buscarPorAutor(@PathVariable String autor) { public ResponseEntity<List<DTORecurso>> buscarPorAutor(@PathVariable String autor) {
try { try {
...@@ -177,7 +126,6 @@ public class RecursosController { ...@@ -177,7 +126,6 @@ public class RecursosController {
} }
} }
@GetMapping("/{id}") @GetMapping("/{id}")
public ResponseEntity<DTORecurso> buscarRecursoPorId(@PathVariable Long id) { public ResponseEntity<DTORecurso> buscarRecursoPorId(@PathVariable Long id) {
try { try {
...@@ -191,33 +139,13 @@ public class RecursosController { ...@@ -191,33 +139,13 @@ public class RecursosController {
} }
} }
@DeleteMapping("/{id}") @DeleteMapping("/{id}")
public ResponseEntity<Void> borrarRecurso(@PathVariable Long id) { public ResponseEntity<Void> borrarRecurso(@PathVariable Long id) {
try { try {
// Buscar el recurso por ID
Recurso recurso = servicioRecursos.buscarRecursoPorId(id); Recurso recurso = servicioRecursos.buscarRecursoPorId(id);
if (recurso == null) { if (recurso == null) {
return ResponseEntity.status(HttpStatus.NOT_FOUND).build(); return ResponseEntity.status(HttpStatus.NOT_FOUND).build();
} }
// Verificar si el recurso tiene una imagen asociada
if (recurso.getFotoUrl() != null && !recurso.getFotoUrl().isEmpty()) {
Path imagePath = Paths.get("uploads/", recurso.getFotoUrl().replace("/uploads/", ""));
// Eliminar l imagen si existe
if (Files.exists(imagePath)) {
try {
Files.delete(imagePath);
} catch (IOException e) {
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).build();
}
}
}
servicioRecursos.borrarRecurso(id); servicioRecursos.borrarRecurso(id);
return ResponseEntity.noContent().build(); return ResponseEntity.noContent().build();
} catch (SecurityException e) { } catch (SecurityException e) {
...@@ -238,63 +166,34 @@ public class RecursosController { ...@@ -238,63 +166,34 @@ public class RecursosController {
return ResponseEntity.status(HttpStatus.NOT_FOUND).build(); return ResponseEntity.status(HttpStatus.NOT_FOUND).build();
} }
String fileName = recursoExistente.getFotoUrl(); // Mantener la imagen actual por defecto // Si hay una nueva imagen, actualizarla
// Si se adjunta una nueva imagen, la procesamos
if (foto != null && !foto.isEmpty()) { if (foto != null && !foto.isEmpty()) {
String mimeType = foto.getContentType(); String mimeType = foto.getContentType();
if (mimeType == null || (!mimeType.equals("image/jpeg") && !mimeType.equals("image/png"))) { if (mimeType == null || (!mimeType.equals("image/jpeg") && !mimeType.equals("image/png"))) {
return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(null); return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(null);
} }
recursoExistente.setFoto(foto.getBytes());
// Eliminar la imagen anterior si existe
if (fileName != null && !fileName.isEmpty()) {
Path oldFilePath = Paths.get("uploads/", fileName);
if (Files.exists(oldFilePath)) {
Files.delete(oldFilePath);
}
}
// Guardar la nueva imagen
String originalFileName = foto.getOriginalFilename();
if (originalFileName != null) {
fileName = originalFileName.replaceAll("\\s+", "_").replaceAll("[^a-zA-Z0-9._-]", "");
}
Path uploadsPath = Paths.get("uploads/");
if (!Files.exists(uploadsPath)) {
Files.createDirectories(uploadsPath);
}
Path filePath = uploadsPath.resolve(fileName);
Files.copy(foto.getInputStream(), filePath, StandardCopyOption.REPLACE_EXISTING);
} }
// Actualizar los datos del recurso // Actualizar otros campos
recursoExistente.setTitulo(dtoRecurso.getTitulo()); recursoExistente.setTitulo(dtoRecurso.getTitulo());
recursoExistente.setDescripcion(dtoRecurso.getDescripcion()); recursoExistente.setDescripcion(dtoRecurso.getDescripcion());
recursoExistente.setFechaPublicacion(dtoRecurso.getFechaPublicacion()); recursoExistente.setFechaPublicacion(dtoRecurso.getFechaPublicacion());
recursoExistente.setAutor(dtoRecurso.getAutor()); recursoExistente.setAutor(dtoRecurso.getAutor());
recursoExistente.setGeneros(dtoRecurso.getGeneros()); // Asignar correctamente los géneros recursoExistente.setGeneros(dtoRecurso.getGeneros());
recursoExistente.setFotoUrl(fileName); // Asignar nueva imagen (o mantener la actual)
// Guardar cambios en la BD // Guardar cambios en la BD
Recurso recursoModificado = servicioRecursos.modificarRecurso(id, recursoExistente); Recurso recursoModificado = servicioRecursos.modificarRecurso(id, recursoExistente);
return ResponseEntity.ok(mapper.dto(recursoModificado)); return ResponseEntity.ok(mapper.dto(recursoModificado));
} catch (RecursoNoExiste e) {
return ResponseEntity.status(HttpStatus.NOT_FOUND).build();
} catch (IOException e) { } catch (IOException e) {
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(null); return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(null);
} catch (SecurityException e) { } catch (RecursoNoExiste e) {
return ResponseEntity.status(HttpStatus.UNAUTHORIZED).build(); return ResponseEntity.status(HttpStatus.NOT_FOUND).build();
} catch (Exception e) {
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).build();
} }
} }
@GetMapping @GetMapping
public ResponseEntity<List<DTORecurso>> obtenerRecursos() { public ResponseEntity<List<DTORecurso>> obtenerRecursos() {
try { try {
...@@ -313,6 +212,7 @@ public class RecursosController { ...@@ -313,6 +212,7 @@ public class RecursosController {
if (recurso == null) { if (recurso == null) {
return ResponseEntity.status(HttpStatus.NOT_FOUND).body("Recurso no encontrado"); return ResponseEntity.status(HttpStatus.NOT_FOUND).body("Recurso no encontrado");
} }
Capitulo nuevoCapitulo = mapper.entity(dtoCapitulo, recurso); Capitulo nuevoCapitulo = mapper.entity(dtoCapitulo, recurso);
servicioRecursos.anadirCapitulo(id, nuevoCapitulo); servicioRecursos.anadirCapitulo(id, nuevoCapitulo);
...@@ -320,12 +220,11 @@ public class RecursosController { ...@@ -320,12 +220,11 @@ public class RecursosController {
} catch (RecursoNoExiste e) { } catch (RecursoNoExiste e) {
return ResponseEntity.status(HttpStatus.NOT_FOUND).body("Recurso no existe"); return ResponseEntity.status(HttpStatus.NOT_FOUND).body("Recurso no existe");
} catch (Exception e) { } catch (Exception e) {
e.printStackTrace(); // Imprime la traza completa del error en la consola e.printStackTrace();
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body("Error interno del servidor: " + e.getMessage()); return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body("Error interno del servidor: " + e.getMessage());
} }
} }
@GetMapping("/{id}/capitulos") @GetMapping("/{id}/capitulos")
public ResponseEntity<List<DTOCapitulo>> obtenerCapitulosDeRecurso(@PathVariable Long id) { public ResponseEntity<List<DTOCapitulo>> obtenerCapitulosDeRecurso(@PathVariable Long id) {
try { try {
...@@ -338,36 +237,80 @@ public class RecursosController { ...@@ -338,36 +237,80 @@ public class RecursosController {
} }
} }
@PutMapping("/{recursoId}/capitulos/{id}") @PutMapping("/{recursoId}/capitulos/{id}")
public ResponseEntity<Void> modificarCapitulo(@PathVariable Long id, @RequestBody DTOCapitulo dtoCapitulo) { public ResponseEntity<DTOCapitulo> modificarCapitulo(
@PathVariable Long recursoId,
@PathVariable Long id,
@RequestBody DTOCapitulo dtoCapitulo) {
if (!id.equals(dtoCapitulo.getId())) {
return ResponseEntity.badRequest().build();
}
try { try {
if (!id.equals(dtoCapitulo.getId())) { Recurso recurso = servicioRecursos.buscarRecursoPorId(recursoId);
return ResponseEntity.badRequest().build(); if (recurso == null) {
return ResponseEntity.notFound().build();
} }
Capitulo capituloExistente = servicioRecursos.buscarCapituloPorId(id); Capitulo capitulo = servicioRecursos.buscarCapituloPorId(id);
if (capituloExistente == null) {
return ResponseEntity.status(HttpStatus.NOT_FOUND).build(); capitulo.setNumero(dtoCapitulo.getNumero());
capitulo.setTitulo(dtoCapitulo.getTitulo());
capitulo.setTipo(dtoCapitulo.getTipo());
capitulo.setActivo(dtoCapitulo.getActivo());
List<FuenteCapitulo> fuentesActuales = new ArrayList<>(capitulo.getFuentes());
List<FuenteCapitulo> fuentesAEliminar = new ArrayList<>();
for (FuenteCapitulo fuenteExistente : fuentesActuales) {
boolean sigueExistiendo = dtoCapitulo.getFuentes().stream()
.anyMatch(f -> f.getNombreFuente().equals(fuenteExistente.getNombreFuente()));
if (!sigueExistiendo) {
fuentesAEliminar.add(fuenteExistente);
}
} }
Capitulo capituloModificado = mapper.entity(dtoCapitulo, capituloExistente.getRecurso()); for (FuenteCapitulo fuente : fuentesAEliminar) {
capituloModificado.setId(id); capitulo.getFuentes().remove(fuente);
servicioRecursos.modificarCapitulo(capituloModificado); servicioRecursos.eliminarFuente(fuente.getId());
}
for (DTOFuenteCapitulo dtoFuente : dtoCapitulo.getFuentes()) {
FuenteCapitulo fuenteExistente = fuentesActuales.stream()
.filter(f -> f.getNombreFuente().equals(dtoFuente.getNombreFuente()))
.findFirst()
.orElse(null);
if (fuenteExistente != null) {
if (!fuenteExistente.getUrlFuente().equals(dtoFuente.getUrlFuente())) {
fuenteExistente.setUrlFuente(dtoFuente.getUrlFuente());
servicioRecursos.actualizarFuente(fuenteExistente);
}
} else {
FuenteCapitulo nuevaFuente = new FuenteCapitulo();
nuevaFuente.setNombreFuente(dtoFuente.getNombreFuente());
nuevaFuente.setUrlFuente(dtoFuente.getUrlFuente());
nuevaFuente.setCapitulo(capitulo);
servicioRecursos.agregarFuente(nuevaFuente);
capitulo.getFuentes().add(nuevaFuente);
}
}
servicioRecursos.modificarCapitulo(capitulo);
return ResponseEntity.ok(mapper.dto(capitulo));
return ResponseEntity.noContent().build();
} catch (CapituloNoExiste e) { } catch (CapituloNoExiste e) {
return ResponseEntity.notFound().build(); return ResponseEntity.notFound().build();
} catch (IllegalStateException e) {
return ResponseEntity.status(HttpStatus.CONFLICT).build();
} catch (SecurityException e) {
return ResponseEntity.status(HttpStatus.UNAUTHORIZED).build();
} catch (Exception e) { } catch (Exception e) {
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).build(); return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).build();
} }
} }
@GetMapping("/{recursoId}/capitulos/{capituloId}") @GetMapping("/{recursoId}/capitulos/{capituloId}")
public ResponseEntity<DTOCapitulo> obtenerCapituloPorId( public ResponseEntity<DTOCapitulo> obtenerCapituloPorId(
@PathVariable Long recursoId, @PathVariable Long recursoId,
...@@ -380,28 +323,24 @@ public class RecursosController { ...@@ -380,28 +323,24 @@ public class RecursosController {
.build(); // Recurso no encontrado .build(); // Recurso no encontrado
} }
// Buscar el capítulo dentro del recurso
Capitulo capitulo = servicioRecursos.buscarCapituloPorId(capituloId); Capitulo capitulo = servicioRecursos.buscarCapituloPorId(capituloId);
if (capitulo == null || !capitulo.getRecurso().getId().equals(recursoId)) { if (capitulo == null || !capitulo.getRecurso().getId().equals(recursoId)) {
return ResponseEntity.status(HttpStatus.NOT_FOUND) return ResponseEntity.status(HttpStatus.NOT_FOUND)
.build(); // Capítulo no encontrado o no pertenece al recurso .build(); // Capítulo no encontrado o no pertenece al recurso
} }
// Convertir a DTO y devolver
return ResponseEntity.ok(mapper.dto(capitulo)); return ResponseEntity.ok(mapper.dto(capitulo));
} catch (Exception e) { } catch (Exception e) {
// Loguear el error para diagnóstico
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).build(); // Error interno del servidor return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).build(); // Error interno del servidor
} }
} }
@GetMapping("/generos") @GetMapping("/generos")
public ResponseEntity<List<String>> obtenerGeneros() { public ResponseEntity<List<String>> obtenerGeneros() {
try { try {
......
package com.ujaen.tfg.mangaffinity.rest;
import org.springframework.core.io.Resource;
import org.springframework.core.io.UrlResource;
import org.springframework.http.HttpHeaders;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
import java.net.MalformedURLException;
import java.nio.file.*;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;
import java.io.IOException;
import java.nio.file.Path;
import java.nio.file.Paths;
@RestController
@RequestMapping("/uploads")
public class UploadController {
private final Path uploadDir = Paths.get("uploads");
@GetMapping("/{filename:.+}")
public ResponseEntity<Resource> getFile(@PathVariable String filename) {
try {
Path file = uploadDir.resolve(filename);
Resource resource = new UrlResource(file.toUri());
if (resource.exists() || resource.isReadable()) {
String contentType = "application/octet-stream";
if (filename.endsWith(".png")) contentType = "image/png";
else if (filename.endsWith(".jpg") || filename.endsWith(".jpeg")) contentType = "image/jpeg";
return ResponseEntity.ok()
.header(HttpHeaders.CONTENT_DISPOSITION, "inline; filename=\"" + resource.getFilename() + "\"")
.header(HttpHeaders.CONTENT_TYPE, contentType)
.body(resource);
} else {
return ResponseEntity.notFound().build();
}
} catch (MalformedURLException e) {
return ResponseEntity.badRequest().build();
}
}
@PostMapping
public ResponseEntity<String> uploadFile(@RequestParam("foto") MultipartFile foto) {
try {
if (foto.isEmpty()) {
return ResponseEntity.status(HttpStatus.BAD_REQUEST).build();
}
String contentType = foto.getContentType();
if (contentType == null || (!contentType.equals("image/jpeg") && !contentType.equals("image/png"))) {
return ResponseEntity.status(HttpStatus.BAD_REQUEST).build();
}
if (!Files.exists(uploadDir)) {
Files.createDirectories(uploadDir);
}
Path path = uploadDir.resolve(foto.getOriginalFilename());
Files.copy(foto.getInputStream(), path, StandardCopyOption.REPLACE_EXISTING);
return ResponseEntity.ok(foto.getOriginalFilename());
} catch (IOException e) {
e.printStackTrace();
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).build();
}
}
}
...@@ -37,8 +37,7 @@ public class ServicioSeguridad { ...@@ -37,8 +37,7 @@ public class ServicioSeguridad {
return config; return config;
})) }))
.authorizeHttpRequests(request -> request .authorizeHttpRequests(request -> request
.requestMatchers(HttpMethod.GET, "/uploads/**").permitAll() .requestMatchers(HttpMethod.POST, "/usuarios/{email}").permitAll()
.requestMatchers(HttpMethod.POST, "/usuarios/{email}").permitAll() // Permitir login sin autenticación
.requestMatchers(HttpMethod.GET, "/usuarios/email/{email}").permitAll() .requestMatchers(HttpMethod.GET, "/usuarios/email/{email}").permitAll()
.requestMatchers(HttpMethod.POST, "/usuarios/").permitAll() .requestMatchers(HttpMethod.POST, "/usuarios/").permitAll()
.requestMatchers(HttpMethod.GET, "/actuator/health").permitAll() .requestMatchers(HttpMethod.GET, "/actuator/health").permitAll()
...@@ -65,7 +64,7 @@ public class ServicioSeguridad { ...@@ -65,7 +64,7 @@ public class ServicioSeguridad {
.anyRequest().authenticated() .anyRequest().authenticated()
) )
.addFilterBefore(new JwtFilter(jwtUtil), UsernamePasswordAuthenticationFilter.class) // Usar solo JWT .addFilterBefore(new JwtFilter(jwtUtil), UsernamePasswordAuthenticationFilter.class)
.build(); .build();
} }
......
package com.ujaen.tfg.mangaffinity.servicios; package com.ujaen.tfg.mangaffinity.servicios;
import com.ujaen.tfg.mangaffinity.entidades.*; import com.ujaen.tfg.mangaffinity.entidades.*;
import com.ujaen.tfg.mangaffinity.excepciones.CapituloNoExiste; import com.ujaen.tfg.mangaffinity.excepciones.CapituloNoExiste;
import com.ujaen.tfg.mangaffinity.excepciones.RecursoNoExiste; import com.ujaen.tfg.mangaffinity.excepciones.RecursoNoExiste;
...@@ -10,24 +9,33 @@ import jakarta.validation.Valid; ...@@ -10,24 +9,33 @@ import jakarta.validation.Valid;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional; import org.springframework.transaction.annotation.Transactional;
import java.time.LocalDate; import java.time.LocalDate;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays; import java.util.Arrays;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.stream.Collectors;
import java.util.stream.Collectors;import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.transaction.annotation.Transactional;
@Service @Service
public class ServicioRecursos { public class ServicioRecursos {
@Autowired @Autowired
RepositorioRecurso repositorioRecurso; RepositorioRecurso repositorioRecurso;
@Autowired @Autowired
RepositorioCapitulo repositorioCapitulo; RepositorioCapitulo repositorioCapitulo;
@Transactional
public void crearRecurso(@Valid Recurso recurso) { public void crearRecurso(@Valid Recurso recurso) {
// Verifica si ya existe un recurso con el mismo título y autor
List<Recurso> recursosExistentes = repositorioRecurso.buscarPorTitulo(recurso.getTitulo());
for (Recurso r : recursosExistentes) {
if (r.getAutor().equals(recurso.getAutor()) &&
r.getFechaPublicacion().equals(recurso.getFechaPublicacion())) {
return;
}
}
repositorioRecurso.crear(recurso); repositorioRecurso.crear(recurso);
} }
...@@ -74,7 +82,6 @@ public class ServicioRecursos { ...@@ -74,7 +82,6 @@ public class ServicioRecursos {
throw new RecursoNoExiste(); throw new RecursoNoExiste();
} }
if (nuevosDatos.getTitulo() != null) { if (nuevosDatos.getTitulo() != null) {
recursoExistente.setTitulo(nuevosDatos.getTitulo()); recursoExistente.setTitulo(nuevosDatos.getTitulo());
} }
...@@ -99,66 +106,19 @@ public class ServicioRecursos { ...@@ -99,66 +106,19 @@ public class ServicioRecursos {
} }
@Transactional @Transactional
public void anadirCapitulo(Long recursoId, Capitulo nuevoCapitulo) { public void actualizarFotoRecurso(Long recursoId, byte[] nuevaFoto) {
Logger logger = LoggerFactory.getLogger(ServicioRecursos.class);
Recurso recurso = repositorioRecurso.buscarPorId(recursoId); Recurso recurso = repositorioRecurso.buscarPorId(recursoId);
if (recurso == null) { if (recurso != null) {
logger.error("❌ Recurso con ID {} no encontrado.", recursoId); recurso.setFoto(nuevaFoto);
throw new RecursoNoExiste(); repositorioRecurso.modificarRecurso(recurso);
}
List<Capitulo> listaCapitulos = (nuevoCapitulo.getTipo() == TipoRecurso.MANGA)
? recurso.getCapitulosManga()
: recurso.getCapitulosAnime();
logger.info("🔍 Lista actual de capítulos para recurso {}: {} capítulos", recursoId, listaCapitulos.size());
if (listaCapitulos.size() < 4) {
logger.info("✅ Espacio disponible, agregando capítulo: {}", nuevoCapitulo.getTitulo());
listaCapitulos.add(nuevoCapitulo);
} else {
logger.warn("⚠️ Lista llena, reemplazando el capítulo más antiguo...");
if (!listaCapitulos.isEmpty()) {
Capitulo capituloAEliminar = listaCapitulos.get(0); // Tomamos el más antiguo
logger.info("📝 Eliminando el capítulo más antiguo: '{}'", capituloAEliminar.getTitulo());
// 🔴 Primero eliminar sus fuentes antes de eliminar el capítulo
repositorioCapitulo.borrarCapitulo(capituloAEliminar);
// ✅ Ahora removerlo de la lista
listaCapitulos.remove(capituloAEliminar);
logger.info("🗑️ Capítulo '{}' eliminado correctamente", capituloAEliminar.getTitulo());
// ✅ Agregar el nuevo capítulo
listaCapitulos.add(nuevoCapitulo);
}
} }
logger.info("✅ Capítulo '{}' añadido correctamente al recurso {}", nuevoCapitulo.getTitulo(), recursoId);
} }
@Transactional(readOnly = true) @Transactional(readOnly = true)
public List<Capitulo> obtenerCapitulosDeRecursoPorTipo(Long recursoId, TipoRecurso tipo) { public List<Capitulo> obtenerCapitulosDeRecursoPorTipo(Long recursoId, TipoRecurso tipo) {
return repositorioCapitulo.obtenerCapitulosPorTipo(recursoId, tipo); return repositorioCapitulo.obtenerCapitulosPorTipo(recursoId, tipo);
} }
@Transactional @Transactional
public List<Capitulo> obtenerCapitulosDeRecurso(Long recursoId) { public List<Capitulo> obtenerCapitulosDeRecurso(Long recursoId) {
List<Capitulo> capitulos = repositorioCapitulo.obtenerCapitulosRecurso(recursoId); List<Capitulo> capitulos = repositorioCapitulo.obtenerCapitulosRecurso(recursoId);
...@@ -167,66 +127,70 @@ public class ServicioRecursos { ...@@ -167,66 +127,70 @@ public class ServicioRecursos {
} }
public List<String> obtenerGeneros() { public List<String> obtenerGeneros() {
return Arrays.stream(Genero.values()) return Arrays.stream(Genero.values())
.map(Enum::name) .map(Enum::name)
.collect(Collectors.toList()); .collect(Collectors.toList());
} }
@Transactional @Transactional
public void anadirCapitulo(Long recursoId, Capitulo nuevoCapitulo) {
Recurso recurso = repositorioRecurso.buscarPorId(recursoId);
if (recurso == null) {
throw new RecursoNoExiste();
}
nuevoCapitulo.setRecurso(recurso);
if (nuevoCapitulo.getFuentes() != null) {
for (FuenteCapitulo fuente : nuevoCapitulo.getFuentes()) {
fuente.setCapitulo(nuevoCapitulo);
}
}
repositorioCapitulo.crearCapitulo(nuevoCapitulo);
}
@Transactional
public void modificarCapitulo(Capitulo capituloModificado) { public void modificarCapitulo(Capitulo capituloModificado) {
Capitulo capitulo = repositorioCapitulo.buscarPorId(capituloModificado.getId()); Capitulo capitulo = repositorioCapitulo.buscarPorId(capituloModificado.getId());
if (capitulo == null) { if (capitulo == null) {
throw new CapituloNoExiste(); throw new CapituloNoExiste();
} }
// Asegurar que el capítulo pertenece a un recurso antes de modificarlo capitulo.setNumero(capituloModificado.getNumero());
if (capitulo.getRecurso() == null) { capitulo.setTitulo(capituloModificado.getTitulo());
throw new IllegalStateException("El capítulo no está asociado a ningún recurso."); capitulo.setTipo(capituloModificado.getTipo());
} capitulo.setActivo(capituloModificado.getActivo());
// Modificar solo si los valores no son nulos o inválidos List<FuenteCapitulo> fuentesActuales = new ArrayList<>(capitulo.getFuentes());
if (capituloModificado.getNumero() > 0) { List<FuenteCapitulo> fuentesAEliminar = new ArrayList<>();
capitulo.setNumero(capituloModificado.getNumero());
}
if (capituloModificado.getTitulo() != null && !capituloModificado.getTitulo().isBlank()) { for (FuenteCapitulo fuenteExistente : fuentesActuales) {
capitulo.setTitulo(capituloModificado.getTitulo()); boolean sigueExistiendo = capituloModificado.getFuentes().stream()
} .anyMatch(f -> f.getNombreFuente().equals(fuenteExistente.getNombreFuente()));
if (capituloModificado.getTipo() != null) { if (!sigueExistiendo) {
capitulo.setTipo(capituloModificado.getTipo()); fuentesAEliminar.add(fuenteExistente);
}
} }
if (capituloModificado.getFuentes() != null) { for (FuenteCapitulo fuente : fuentesAEliminar) {
// Crear un mapa con las fuentes existentes para facilitar la comparación repositorioCapitulo.eliminarFuente(fuente);
Map<Long, FuenteCapitulo> fuentesExistentes = capitulo.getFuentes().stream() capitulo.getFuentes().remove(fuente);
.collect(Collectors.toMap(FuenteCapitulo::getId, f -> f)); }
List<FuenteCapitulo> nuevasFuentes = new ArrayList<>(); for (FuenteCapitulo nuevaFuente : capituloModificado.getFuentes()) {
boolean existe = fuentesActuales.stream()
.anyMatch(f -> f.getNombreFuente().equals(nuevaFuente.getNombreFuente()));
for (FuenteCapitulo fuenteNueva : capituloModificado.getFuentes()) { if (!existe) {
if (fuenteNueva.getId() != null && fuentesExistentes.containsKey(fuenteNueva.getId())) { nuevaFuente.setCapitulo(capitulo);
// Si la fuente ya existe, actualizamos su información repositorioCapitulo.agregarFuente(nuevaFuente);
FuenteCapitulo fuenteExistente = fuentesExistentes.get(fuenteNueva.getId()); capitulo.getFuentes().add(nuevaFuente);
fuenteExistente.setNombreFuente(fuenteNueva.getNombreFuente());
fuenteExistente.setUrlFuente(fuenteNueva.getUrlFuente());
nuevasFuentes.add(fuenteExistente);
} else {
// Si la fuente es nueva, simplemente la agregamos a la lista del capítulo
nuevasFuentes.add(fuenteNueva);
}
} }
// 🔴 Remover las fuentes que ya no están en la lista
capitulo.getFuentes().clear();
capitulo.getFuentes().addAll(nuevasFuentes);
} }
repositorioCapitulo.actualizarCapitulo(capitulo); repositorioCapitulo.actualizarCapitulo(capitulo);
} }
@Transactional(readOnly = true) @Transactional(readOnly = true)
public Capitulo buscarCapituloPorId(Long id) { public Capitulo buscarCapituloPorId(Long id) {
Capitulo capitulo = repositorioCapitulo.buscarPorId(id); Capitulo capitulo = repositorioCapitulo.buscarPorId(id);
...@@ -236,6 +200,33 @@ public class ServicioRecursos { ...@@ -236,6 +200,33 @@ public class ServicioRecursos {
return capitulo; return capitulo;
} }
@Transactional
public void actualizarFuente(FuenteCapitulo fuente) {
FuenteCapitulo fuenteExistente = repositorioCapitulo.buscarFuentePorId(fuente.getId());
if (fuenteExistente != null) {
fuenteExistente.setUrlFuente(fuente.getUrlFuente());
repositorioCapitulo.actualizarFuente(fuenteExistente);
}
}
@Transactional
public void eliminarFuentePorId(Long id) {
FuenteCapitulo fuente = repositorioCapitulo.buscarFuentePorId(id);
if (fuente != null) {
repositorioCapitulo.eliminarFuentePorId(id);
}
}
@Transactional
public void agregarFuente(FuenteCapitulo fuente) {
repositorioCapitulo.agregarFuente(fuente);
}
@Transactional
public void eliminarFuente(Long id) {
FuenteCapitulo fuente = repositorioCapitulo.buscarFuentePorId(id);
if (fuente != null) {
repositorioCapitulo.eliminarFuentePorId(id);
}
}
} }
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or sign in to comment