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;
import jakarta.persistence.PersistenceContext;
import org.springframework.stereotype.Repository;
import org.springframework.transaction.annotation.Transactional;
import java.time.LocalDate;
import java.util.List;
......@@ -28,17 +27,16 @@ public class RepositorioRecurso {
@Transactional(readOnly = true)
public List<Recurso> buscarPorTitulo(String titulo) {
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)
.setParameter("titulo", "%" + titulo + "%")
.getResultList();
}
@Transactional(readOnly = true)
public List<Recurso> buscarPorAutor(String autor) {
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)
.setParameter("autor", "%" + autor + "%")
.getResultList();
......@@ -47,7 +45,7 @@ public class RepositorioRecurso {
@Transactional(readOnly = true)
public List<Recurso> buscarPorGenero(Genero genero) {
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)
.setParameter("genero", genero)
.getResultList();
......@@ -56,7 +54,7 @@ public class RepositorioRecurso {
@Transactional(readOnly = true)
public List<Recurso> buscarPorRangoFechas(LocalDate fechaInicio, LocalDate fechaFin) {
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)
.setParameter("inicio", fechaInicio)
.setParameter("fin", fechaFin)
......
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.BibliotecaPersonalRecurso;
import com.ujaen.tfg.mangaffinity.entidades.Categoria;
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.Mapper;
import com.ujaen.tfg.mangaffinity.servicios.ServicioBibliotecaPersonal;
import com.ujaen.tfg.mangaffinity.servicios.ServicioRecursos;
import com.ujaen.tfg.mangaffinity.servicios.ServicioUsuarios;
......@@ -15,14 +12,14 @@ import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
import java.util.Base64;
import java.util.List;
import java.util.stream.Collectors;
@RestController
@RequestMapping("/biblioteca")
public class BibliotecaPersonalController {
private static final Logger logger = LoggerFactory.getLogger(BibliotecaPersonalController.class);
@Autowired
private ServicioBibliotecaPersonal servicioBibliotecaPersonal;
......@@ -32,9 +29,6 @@ public class BibliotecaPersonalController {
@Autowired
private ServicioUsuarios servicioUsuarios;
@Autowired
private Mapper mapper;
@PostMapping("/{usuarioId}/recursos/{recursoId}/categoria")
public ResponseEntity<String> anadirRecursoBiblioteca(
@PathVariable Long usuarioId,
......@@ -51,7 +45,6 @@ public class BibliotecaPersonalController {
return ResponseEntity.status(HttpStatus.NOT_FOUND).build();
}
boolean yaExiste = servicioBibliotecaPersonal.listarPorCategoria(biblioteca.getId(), dtoRecursoEnBiblioteca.getCategoria())
.stream()
.anyMatch(bpr -> bpr.getRecurso().getId().equals(recursoId));
......@@ -67,11 +60,9 @@ public class BibliotecaPersonalController {
}
}
@GetMapping("/{usuarioId}/recursos/categoria/{categoria}")
public ResponseEntity<List<DTORecursoEnBiblioteca>> listarRecursosPorCategoria(
@PathVariable Long usuarioId, @PathVariable String categoria) { // 🔥 Ahora es String
@PathVariable Long usuarioId, @PathVariable String categoria) {
try {
BibliotecaPersonal biblioteca = servicioUsuarios.obtenerBibliotecaDeUsuario(usuarioId);
if (biblioteca == null) {
......@@ -82,17 +73,23 @@ public class BibliotecaPersonalController {
try {
categoriaEnum = Categoria.valueOf(categoria.toUpperCase());
} 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<DTORecursoEnBiblioteca> dtoRecursos = recursos.stream()
.map(bpr -> new DTORecursoEnBiblioteca(
bpr.getRecurso().getId(),
bpr.getRecurso().getTitulo(),
bpr.getCategoria(),
bpr.getRecurso().getFotoUrl() // 🔥 Aquí añadimos la imagen
))
.map(bpr -> {
String fotoBase64 = (bpr.getRecurso().getFoto() != null && bpr.getRecurso().getFoto().length > 0)
? "data:image/jpeg;base64," + Base64.getEncoder().encodeToString(bpr.getRecurso().getFoto())
: null;
return new DTORecursoEnBiblioteca(
bpr.getRecurso().getId(),
bpr.getRecurso().getTitulo(),
bpr.getCategoria(),
fotoBase64
);
})
.collect(Collectors.toList());
......@@ -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}")
public ResponseEntity<String> eliminarRecursoDeBiblioteca(
@PathVariable Long usuarioId,
......@@ -127,7 +108,7 @@ public class BibliotecaPersonalController {
if (biblioteca == null) return ResponseEntity.status(HttpStatus.NOT_FOUND).build();
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) {
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).build();
}
......@@ -137,38 +118,44 @@ public class BibliotecaPersonalController {
public ResponseEntity<DTORecursoEnBiblioteca> obtenerRecursoEnBiblioteca(
@PathVariable Long usuarioId, @PathVariable Long recursoId) {
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);
if (biblioteca == null) {
logger.warn("No se encontró la biblioteca para el usuario con ID: {}", usuarioId);
return ResponseEntity.status(HttpStatus.NOT_FOUND).body(null); // Si no existe la biblioteca del usuario
return ResponseEntity.status(HttpStatus.NOT_FOUND).body(null);
}
// Buscar el recurso dentro de la biblioteca del usuario
BibliotecaPersonalRecurso bibliotecaPersonalRecurso = servicioBibliotecaPersonal.buscarRecursoEnBiblioteca(biblioteca.getId(), recursoId);
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); // Si no se encuentra el recurso en la biblioteca
return ResponseEntity.status(HttpStatus.NOT_FOUND).body(null);
}
// Mapear el recurso encontrado al DTO para retornar
DTORecursoEnBiblioteca dtoRecursoEnBiblioteca = new DTORecursoEnBiblioteca(
bibliotecaPersonalRecurso.getRecurso().getId(),
bibliotecaPersonalRecurso.getRecurso().getTitulo(),
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);
} 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(); // Si ocurre algún error inesperado
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).build();
}
}
@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;
import com.ujaen.tfg.mangaffinity.entidades.Genero;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.NotNull;
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
import java.time.LocalDate;
import java.util.HashSet;
import java.util.Set;
@Getter
......@@ -17,19 +15,11 @@ import java.util.Set;
@AllArgsConstructor
public class DTORecurso {
private Long id;
@NotBlank
private String titulo;
private String descripcion;
@NotNull
private LocalDate fechaPublicacion;
@NotBlank
private String autor;
private String fotoUrl;
private Set<Genero> generos = new HashSet<>();
private String fotoBase64;
private Set<Genero> generos;
}
......@@ -11,16 +11,8 @@ import lombok.Setter;
@NoArgsConstructor
@AllArgsConstructor
public class DTORecursoEnBiblioteca {
private Long usuarioId;
private Long recursoId;
private String titulo;
private Categoria categoria;
private String fotoUrl;
public DTORecursoEnBiblioteca(Long recursoId, String titulo, Categoria categoria, String fotoUrl) {
this.recursoId = recursoId;
this.titulo = titulo;
this.categoria = categoria;
this.fotoUrl = fotoUrl;
}
private String fotoBase64;
}
......@@ -3,53 +3,26 @@ package com.ujaen.tfg.mangaffinity.rest;
import com.ujaen.tfg.mangaffinity.entidades.*;
import com.ujaen.tfg.mangaffinity.excepciones.CapituloNoExiste;
import com.ujaen.tfg.mangaffinity.excepciones.RecursoNoExiste;
import com.ujaen.tfg.mangaffinity.rest.DTO.DTOCapitulo;
import com.ujaen.tfg.mangaffinity.rest.DTO.DTORecurso;
import com.ujaen.tfg.mangaffinity.rest.DTO.Mapper;
import com.ujaen.tfg.mangaffinity.rest.DTO.*;
import com.ujaen.tfg.mangaffinity.servicios.ServicioRecursos;
import jakarta.validation.Valid;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
import java.time.LocalDate;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.io.File;import org.springframework.web.multipart.MultipartFile;import com.ujaen.tfg.mangaffinity.entidades.Recurso;
import java.util.*;
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.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.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
@RequestMapping("/recursos")
public class RecursosController {
@Autowired
private ServicioRecursos servicioRecursos;
@Autowired
private Mapper mapper;
......@@ -57,50 +30,31 @@ public class RecursosController {
public ResponseEntity<String> crearRecurso(
@RequestPart("recurso") DTORecurso recursoDTO,
@RequestPart(value = "foto", required = false) MultipartFile foto) {
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()) {
String mimeType = foto.getContentType();
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.");
}
// 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);
fotoBytes = foto.getBytes();
}
// Crear el recurso con solo el nombre del archivo en la BD
Recurso nuevoRecurso = new Recurso(
recursoDTO.getTitulo(),
recursoDTO.getDescripcion(),
recursoDTO.getFechaPublicacion(),
recursoDTO.getAutor(),
fileName // Guardamos solo el nombre del archivo
fotoBytes
);
nuevoRecurso.setGeneros(recursoDTO.getGeneros());
servicioRecursos.crearRecurso(nuevoRecurso);
return ResponseEntity.status(HttpStatus.CREATED).build();
} catch (IOException e) {
......@@ -108,9 +62,6 @@ public class RecursosController {
}
}
@GetMapping("/titulo/{titulo}")
public ResponseEntity<List<DTORecurso>> buscarPorTitulo(@PathVariable String titulo) {
try {
......@@ -125,7 +76,6 @@ public class RecursosController {
}
}
@GetMapping("/{id}/capitulos/tipos")
public ResponseEntity<Map<String, List<DTOCapitulo>>> obtenerCapitulosPorTipo(@PathVariable Long id) {
try {
......@@ -142,7 +92,7 @@ public class RecursosController {
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).build();
}
}
@GetMapping("/genero/{genero}")
public ResponseEntity<List<DTORecurso>> buscarPorGenero(@PathVariable Genero genero) {
try {
......@@ -165,7 +115,6 @@ public class RecursosController {
}
}
@GetMapping("/autor/{autor}")
public ResponseEntity<List<DTORecurso>> buscarPorAutor(@PathVariable String autor) {
try {
......@@ -177,7 +126,6 @@ public class RecursosController {
}
}
@GetMapping("/{id}")
public ResponseEntity<DTORecurso> buscarRecursoPorId(@PathVariable Long id) {
try {
......@@ -191,33 +139,13 @@ public class RecursosController {
}
}
@DeleteMapping("/{id}")
public ResponseEntity<Void> borrarRecurso(@PathVariable Long id) {
try {
// Buscar el recurso por ID
Recurso recurso = servicioRecursos.buscarRecursoPorId(id);
if (recurso == null) {
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);
return ResponseEntity.noContent().build();
} catch (SecurityException e) {
......@@ -238,63 +166,34 @@ public class RecursosController {
return ResponseEntity.status(HttpStatus.NOT_FOUND).build();
}
String fileName = recursoExistente.getFotoUrl(); // Mantener la imagen actual por defecto
// Si se adjunta una nueva imagen, la procesamos
// Si hay una nueva imagen, actualizarla
if (foto != null && !foto.isEmpty()) {
String mimeType = foto.getContentType();
if (mimeType == null || (!mimeType.equals("image/jpeg") && !mimeType.equals("image/png"))) {
return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(null);
}
// 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);
recursoExistente.setFoto(foto.getBytes());
}
// Actualizar los datos del recurso
// Actualizar otros campos
recursoExistente.setTitulo(dtoRecurso.getTitulo());
recursoExistente.setDescripcion(dtoRecurso.getDescripcion());
recursoExistente.setFechaPublicacion(dtoRecurso.getFechaPublicacion());
recursoExistente.setAutor(dtoRecurso.getAutor());
recursoExistente.setGeneros(dtoRecurso.getGeneros()); // Asignar correctamente los géneros
recursoExistente.setFotoUrl(fileName); // Asignar nueva imagen (o mantener la actual)
recursoExistente.setGeneros(dtoRecurso.getGeneros());
// Guardar cambios en la BD
Recurso recursoModificado = servicioRecursos.modificarRecurso(id, recursoExistente);
return ResponseEntity.ok(mapper.dto(recursoModificado));
} catch (RecursoNoExiste e) {
return ResponseEntity.status(HttpStatus.NOT_FOUND).build();
} catch (IOException e) {
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(null);
} catch (SecurityException e) {
return ResponseEntity.status(HttpStatus.UNAUTHORIZED).build();
} catch (Exception e) {
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).build();
} catch (RecursoNoExiste e) {
return ResponseEntity.status(HttpStatus.NOT_FOUND).build();
}
}
@GetMapping
public ResponseEntity<List<DTORecurso>> obtenerRecursos() {
try {
......@@ -313,6 +212,7 @@ public class RecursosController {
if (recurso == null) {
return ResponseEntity.status(HttpStatus.NOT_FOUND).body("Recurso no encontrado");
}
Capitulo nuevoCapitulo = mapper.entity(dtoCapitulo, recurso);
servicioRecursos.anadirCapitulo(id, nuevoCapitulo);
......@@ -320,12 +220,11 @@ public class RecursosController {
} catch (RecursoNoExiste e) {
return ResponseEntity.status(HttpStatus.NOT_FOUND).body("Recurso no existe");
} 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());
}
}
@GetMapping("/{id}/capitulos")
public ResponseEntity<List<DTOCapitulo>> obtenerCapitulosDeRecurso(@PathVariable Long id) {
try {
......@@ -338,36 +237,80 @@ public class RecursosController {
}
}
@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 {
if (!id.equals(dtoCapitulo.getId())) {
return ResponseEntity.badRequest().build();
Recurso recurso = servicioRecursos.buscarRecursoPorId(recursoId);
if (recurso == null) {
return ResponseEntity.notFound().build();
}
Capitulo capituloExistente = servicioRecursos.buscarCapituloPorId(id);
if (capituloExistente == null) {
return ResponseEntity.status(HttpStatus.NOT_FOUND).build();
Capitulo capitulo = servicioRecursos.buscarCapituloPorId(id);
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());
capituloModificado.setId(id);
servicioRecursos.modificarCapitulo(capituloModificado);
for (FuenteCapitulo fuente : fuentesAEliminar) {
capitulo.getFuentes().remove(fuente);
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) {
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) {
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).build();
}
}
@GetMapping("/{recursoId}/capitulos/{capituloId}")
public ResponseEntity<DTOCapitulo> obtenerCapituloPorId(
@PathVariable Long recursoId,
......@@ -380,28 +323,24 @@ public class RecursosController {
.build(); // Recurso no encontrado
}
// Buscar el capítulo dentro del recurso
Capitulo capitulo = servicioRecursos.buscarCapituloPorId(capituloId);
if (capitulo == null || !capitulo.getRecurso().getId().equals(recursoId)) {
return ResponseEntity.status(HttpStatus.NOT_FOUND)
.build(); // Capítulo no encontrado o no pertenece al recurso
}
// Convertir a DTO y devolver
return ResponseEntity.ok(mapper.dto(capitulo));
} catch (Exception e) {
// Loguear el error para diagnóstico
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).build(); // Error interno del servidor
}
}
@GetMapping("/generos")
public ResponseEntity<List<String>> obtenerGeneros() {
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 {
return config;
}))
.authorizeHttpRequests(request -> request
.requestMatchers(HttpMethod.GET, "/uploads/**").permitAll()
.requestMatchers(HttpMethod.POST, "/usuarios/{email}").permitAll() // Permitir login sin autenticación
.requestMatchers(HttpMethod.POST, "/usuarios/{email}").permitAll()
.requestMatchers(HttpMethod.GET, "/usuarios/email/{email}").permitAll()
.requestMatchers(HttpMethod.POST, "/usuarios/").permitAll()
.requestMatchers(HttpMethod.GET, "/actuator/health").permitAll()
......@@ -65,7 +64,7 @@ public class ServicioSeguridad {
.anyRequest().authenticated()
)
.addFilterBefore(new JwtFilter(jwtUtil), UsernamePasswordAuthenticationFilter.class) // Usar solo JWT
.addFilterBefore(new JwtFilter(jwtUtil), UsernamePasswordAuthenticationFilter.class)
.build();
}
......
package com.ujaen.tfg.mangaffinity.servicios;
import com.ujaen.tfg.mangaffinity.entidades.*;
import com.ujaen.tfg.mangaffinity.excepciones.CapituloNoExiste;
import com.ujaen.tfg.mangaffinity.excepciones.RecursoNoExiste;
......@@ -10,24 +9,33 @@ import jakarta.validation.Valid;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.time.LocalDate;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.transaction.annotation.Transactional;
import java.util.stream.Collectors;
@Service
public class ServicioRecursos {
@Autowired
RepositorioRecurso repositorioRecurso;
@Autowired
RepositorioCapitulo repositorioCapitulo;
@Transactional
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);
}
......@@ -74,7 +82,6 @@ public class ServicioRecursos {
throw new RecursoNoExiste();
}
if (nuevosDatos.getTitulo() != null) {
recursoExistente.setTitulo(nuevosDatos.getTitulo());
}
......@@ -99,66 +106,19 @@ public class ServicioRecursos {
}
@Transactional
public void anadirCapitulo(Long recursoId, Capitulo nuevoCapitulo) {
Logger logger = LoggerFactory.getLogger(ServicioRecursos.class);
public void actualizarFotoRecurso(Long recursoId, byte[] nuevaFoto) {
Recurso recurso = repositorioRecurso.buscarPorId(recursoId);
if (recurso == null) {
logger.error("❌ Recurso con ID {} no encontrado.", recursoId);
throw new RecursoNoExiste();
}
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);
}
if (recurso != null) {
recurso.setFoto(nuevaFoto);
repositorioRecurso.modificarRecurso(recurso);
}
logger.info("✅ Capítulo '{}' añadido correctamente al recurso {}", nuevoCapitulo.getTitulo(), recursoId);
}
@Transactional(readOnly = true)
public List<Capitulo> obtenerCapitulosDeRecursoPorTipo(Long recursoId, TipoRecurso tipo) {
return repositorioCapitulo.obtenerCapitulosPorTipo(recursoId, tipo);
}
@Transactional
public List<Capitulo> obtenerCapitulosDeRecurso(Long recursoId) {
List<Capitulo> capitulos = repositorioCapitulo.obtenerCapitulosRecurso(recursoId);
......@@ -167,66 +127,70 @@ public class ServicioRecursos {
}
public List<String> obtenerGeneros() {
return Arrays.stream(Genero.values())
.map(Enum::name)
.collect(Collectors.toList());
}
@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) {
Capitulo capitulo = repositorioCapitulo.buscarPorId(capituloModificado.getId());
if (capitulo == null) {
throw new CapituloNoExiste();
}
// Asegurar que el capítulo pertenece a un recurso antes de modificarlo
if (capitulo.getRecurso() == null) {
throw new IllegalStateException("El capítulo no está asociado a ningún recurso.");
}
capitulo.setNumero(capituloModificado.getNumero());
capitulo.setTitulo(capituloModificado.getTitulo());
capitulo.setTipo(capituloModificado.getTipo());
capitulo.setActivo(capituloModificado.getActivo());
// Modificar solo si los valores no son nulos o inválidos
if (capituloModificado.getNumero() > 0) {
capitulo.setNumero(capituloModificado.getNumero());
}
List<FuenteCapitulo> fuentesActuales = new ArrayList<>(capitulo.getFuentes());
List<FuenteCapitulo> fuentesAEliminar = new ArrayList<>();
if (capituloModificado.getTitulo() != null && !capituloModificado.getTitulo().isBlank()) {
capitulo.setTitulo(capituloModificado.getTitulo());
}
for (FuenteCapitulo fuenteExistente : fuentesActuales) {
boolean sigueExistiendo = capituloModificado.getFuentes().stream()
.anyMatch(f -> f.getNombreFuente().equals(fuenteExistente.getNombreFuente()));
if (capituloModificado.getTipo() != null) {
capitulo.setTipo(capituloModificado.getTipo());
if (!sigueExistiendo) {
fuentesAEliminar.add(fuenteExistente);
}
}
if (capituloModificado.getFuentes() != null) {
// Crear un mapa con las fuentes existentes para facilitar la comparación
Map<Long, FuenteCapitulo> fuentesExistentes = capitulo.getFuentes().stream()
.collect(Collectors.toMap(FuenteCapitulo::getId, f -> f));
for (FuenteCapitulo fuente : fuentesAEliminar) {
repositorioCapitulo.eliminarFuente(fuente);
capitulo.getFuentes().remove(fuente);
}
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 (fuenteNueva.getId() != null && fuentesExistentes.containsKey(fuenteNueva.getId())) {
// Si la fuente ya existe, actualizamos su información
FuenteCapitulo fuenteExistente = fuentesExistentes.get(fuenteNueva.getId());
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);
}
if (!existe) {
nuevaFuente.setCapitulo(capitulo);
repositorioCapitulo.agregarFuente(nuevaFuente);
capitulo.getFuentes().add(nuevaFuente);
}
// 🔴 Remover las fuentes que ya no están en la lista
capitulo.getFuentes().clear();
capitulo.getFuentes().addAll(nuevasFuentes);
}
repositorioCapitulo.actualizarCapitulo(capitulo);
}
@Transactional(readOnly = true)
public Capitulo buscarCapituloPorId(Long id) {
Capitulo capitulo = repositorioCapitulo.buscarPorId(id);
......@@ -236,6 +200,33 @@ public class ServicioRecursos {
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