Commit f6093cdb by Antonio Rueda
parents a8cd1c43 fd486b02
...@@ -5,7 +5,10 @@ import org.springframework.boot.SpringApplication; ...@@ -5,7 +5,10 @@ import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.autoconfigure.domain.EntityScan; import org.springframework.boot.autoconfigure.domain.EntityScan;
import org.springframework.cache.annotation.EnableCaching; import org.springframework.cache.annotation.EnableCaching;
import org.springframework.context.annotation.Bean;
import org.springframework.scheduling.annotation.EnableScheduling; import org.springframework.scheduling.annotation.EnableScheduling;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
/** /**
* *
...@@ -20,6 +23,11 @@ import org.springframework.scheduling.annotation.EnableScheduling; ...@@ -20,6 +23,11 @@ import org.springframework.scheduling.annotation.EnableScheduling;
@EnableScheduling @EnableScheduling
@EnableCaching @EnableCaching
public class ReservaHoteles { public class ReservaHoteles {
@Bean
PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
public static void main(String[] args) { public static void main(String[] args) {
SpringApplication.run(ReservaHoteles.class); SpringApplication.run(ReservaHoteles.class);
} }
......
...@@ -24,6 +24,9 @@ import java.util.List; ...@@ -24,6 +24,9 @@ import java.util.List;
*/ */
@Entity @Entity
public class Hotel { public class Hotel {
public static record Disponibilidad(int numHabSimple, int numHabDoble) {}
@Id @Id
@GeneratedValue(strategy=GenerationType.IDENTITY) @GeneratedValue(strategy=GenerationType.IDENTITY)
int id; int id;
...@@ -102,14 +105,12 @@ public class Hotel { ...@@ -102,14 +105,12 @@ public class Hotel {
} }
/** /**
* Comprueba si hay disponibilidad en las fechas indicadas * Devuelve la disponibilidad en las fechas indicadas
* @param fechaInicio fecha de inicio de la reserva * @param fechaInicio fecha de inicio de la reserva
* @param fechaFin fecha de final de la reserva * @param fechaFin fecha de final de la reserva
* @param numHabSimple número de habitaciones simples solicitadas * @return la disponibilidad en las fechas indicadas
* @param numHabDoble número de habitaciones dobles solicitadas
* @return true si hay disponibilidad, false en caso contrario
*/ */
public boolean disponible(LocalDate fechaInicio, LocalDate fechaFin, int numHabSimple, int numHabDoble) { public Disponibilidad disponibilidad(LocalDate fechaInicio, LocalDate fechaFin) {
// Obtener la fecha máxima donde hay registrada una reserva // Obtener la fecha máxima donde hay registrada una reserva
final var fechaMax = reservas.stream() final var fechaMax = reservas.stream()
.map(r -> r.fechaFin) .map(r -> r.fechaFin)
...@@ -117,21 +118,35 @@ public class Hotel { ...@@ -117,21 +118,35 @@ public class Hotel {
.map(f -> f.isBefore(fechaFin) ? f : fechaFin) .map(f -> f.isBefore(fechaFin) ? f : fechaFin)
.orElse(LocalDate.now()); .orElse(LocalDate.now());
for (var fecha = fechaInicio.plusDays(0); fecha.isBefore(fechaMax); fecha = fecha.plusDays(1)) int numHabSimpleDisponibles = numHabSimple;
if (!disponible(fecha, numHabSimple, numHabDoble)) int numHabDobleDisponibles = numHabDoble;
return false; for (var fecha = fechaInicio.plusDays(0); fecha.isBefore(fechaMax); fecha = fecha.plusDays(1)) {
var disponibilidadDia = disponibilidad(fecha);
return true; numHabSimpleDisponibles = Integer.min(numHabSimpleDisponibles, disponibilidadDia.numHabSimple);
numHabDobleDisponibles = Integer.min(numHabDobleDisponibles, disponibilidadDia.numHabDoble);
}
return new Disponibilidad(numHabSimpleDisponibles, numHabDobleDisponibles);
} }
/** /**
* Comprueba si hay disponibilidad en un día concreto * Indica si el hotel está disponible en las fechas indicadas
* @param fecha día a comprobar * @param fechaInicio fecha de inicio de la reserva
* @param fechaFin fecha de final de la reserva
* @param numHabSimple número de habitaciones simples solicitadas * @param numHabSimple número de habitaciones simples solicitadas
* @param numHabDoble número de habitaciones dobles solicitadas * @param numHabDoble número de habitaciones dobles solicitadas
* @return true si hay disponibilidad, false en caso contrario * @return true si el hotel está disponible
*/
public boolean disponible(LocalDate fechaInicio, LocalDate fechaFin, int numHabSimple, int numHabDoble) {
var disponibilidad = disponibilidad(fechaInicio, fechaFin);
return disponibilidad.numHabSimple >= numHabSimple && disponibilidad.numHabDoble > numHabDoble;
}
/**
* Devuelve la disponibilidad en un día concreto
* @param fecha día a comprobar
* @return la disponibilidad en la fecha dada
*/ */
public boolean disponible(LocalDate fecha, int numHabSimple, int numHabDoble) { public Disponibilidad disponibilidad(LocalDate fecha) {
int numHabSimpleDisponibles = numHabSimple; int numHabSimpleDisponibles = numHabSimple;
int numHabDobleDisponibles = numHabDoble; int numHabDobleDisponibles = numHabDoble;
...@@ -142,7 +157,7 @@ public class Hotel { ...@@ -142,7 +157,7 @@ public class Hotel {
} }
} }
return numHabSimpleDisponibles >= numHabSimple && numHabDobleDisponibles >= numHabDoble; return new Disponibilidad(numHabSimpleDisponibles, numHabDobleDisponibles);
} }
/** /**
......
package es.ujaen.dae.reservahoteles.excepciones;
/**
*
* @author ajrueda
*/
public class HotelNoRegistrado extends RuntimeException {
public HotelNoRegistrado() {
}
}
...@@ -3,9 +3,12 @@ package es.ujaen.dae.reservahoteles.rest; ...@@ -3,9 +3,12 @@ package es.ujaen.dae.reservahoteles.rest;
import es.ujaen.dae.reservahoteles.entidades.Hotel; import es.ujaen.dae.reservahoteles.entidades.Hotel;
import es.ujaen.dae.reservahoteles.entidades.Usuario; import es.ujaen.dae.reservahoteles.entidades.Usuario;
import es.ujaen.dae.reservahoteles.excepciones.HotelNoRegistrado;
import es.ujaen.dae.reservahoteles.excepciones.UsuarioNoRegistrado; import es.ujaen.dae.reservahoteles.excepciones.UsuarioNoRegistrado;
import es.ujaen.dae.reservahoteles.excepciones.UsuarioYaRegistrado; import es.ujaen.dae.reservahoteles.excepciones.UsuarioYaRegistrado;
import es.ujaen.dae.reservahoteles.rest.dto.DDisponibilidad;
import es.ujaen.dae.reservahoteles.rest.dto.DHotel; import es.ujaen.dae.reservahoteles.rest.dto.DHotel;
import es.ujaen.dae.reservahoteles.rest.dto.DReserva;
import es.ujaen.dae.reservahoteles.rest.dto.DUsuario; import es.ujaen.dae.reservahoteles.rest.dto.DUsuario;
import es.ujaen.dae.reservahoteles.rest.dto.Mapeador; import es.ujaen.dae.reservahoteles.rest.dto.Mapeador;
import es.ujaen.dae.reservahoteles.servicios.ServicioReservas; import es.ujaen.dae.reservahoteles.servicios.ServicioReservas;
...@@ -97,7 +100,7 @@ public class ControladorReservas { ...@@ -97,7 +100,7 @@ public class ControladorReservas {
List<Hotel> hoteles; List<Hotel> hoteles;
if (nombre != null) { if (nombre != null) {
hoteles = servicioReservas.buscarHotel(nombre, localidad).stream() hoteles = servicioReservas.buscarHotel(nombre, localidad).stream()
.filter(h -> servicioReservas.disponible(h, desdeFinal, hastaFinal, numHabSimple, numHabDoble)).toList(); .filter(h -> h.disponible(desdeFinal, hastaFinal, numHabSimple, numHabDoble)).toList();
} }
else { else {
hoteles = servicioReservas.buscarHotelesDisponiblesPorLocalidad(localidad, desdeFinal, hastaFinal, numHabSimple, numHabDoble); hoteles = servicioReservas.buscarHotelesDisponiblesPorLocalidad(localidad, desdeFinal, hastaFinal, numHabSimple, numHabDoble);
...@@ -105,4 +108,52 @@ public class ControladorReservas { ...@@ -105,4 +108,52 @@ public class ControladorReservas {
return ResponseEntity.ok(hoteles.stream().map(h -> mapeador.dto(h)).toList()); return ResponseEntity.ok(hoteles.stream().map(h -> mapeador.dto(h)).toList());
} }
@GetMapping("/hoteles/{id}")
public ResponseEntity<DHotel> buscarHotel(@PathVariable int id) {
try {
Hotel hotel = servicioReservas.buscarHotel(id).orElseThrow(HotelNoRegistrado::new);
return ResponseEntity.ok(mapeador.dto(hotel));
}
catch(HotelNoRegistrado e) {
return ResponseEntity.status(HttpStatus.NOT_FOUND).build();
}
}
@GetMapping("/hoteles/{id}/disponibilidad")
public ResponseEntity<DDisponibilidad> verDisponibilidadHotel(@PathVariable int id,
@RequestParam LocalDate desde,
@RequestParam LocalDate hasta) {
final var desdeFinal = desde != null ? desde : LocalDate.now();
final var hastaFinal = hasta != null ? hasta : LocalDate.MAX;
try {
Hotel hotel = servicioReservas.buscarHotel(id).orElseThrow(HotelNoRegistrado::new);
return ResponseEntity.ok(mapeador.dto(hotel.disponibilidad(desdeFinal, hastaFinal)));
}
catch(HotelNoRegistrado e) {
return ResponseEntity.status(HttpStatus.NOT_FOUND).build();
}
}
@PostMapping("/hoteles/{id}/reservas")
public ResponseEntity<DReserva> reserva(@PathVariable int id, @RequestBody DReserva reserva) {
try {
Hotel hotel = servicioReservas.buscarHotel(id).orElseThrow(HotelNoRegistrado::new);
Usuario usuario = servicioReservas.buscarUsuario(reserva.emailUsuario()).orElseThrow(UsuarioNoRegistrado::new);
return ResponseEntity.status(HttpStatus.CREATED).body(mapeador.dto(servicioReservas.reserva(
usuario,
hotel,
reserva.fechaInicio(),
reserva.fechaFin(),
reserva.numHabSimple(),
reserva.numHabDoble()
)));
}
catch(HotelNoRegistrado | UsuarioNoRegistrado e) {
return ResponseEntity.status(HttpStatus.NOT_FOUND).build();
}
}
} }
/*
* Click nbfs://nbhost/SystemFileSystem/Templates/Licenses/license-default.txt to change this license
* Click nbfs://nbhost/SystemFileSystem/Templates/Classes/Record.java to edit this template
*/
package es.ujaen.dae.reservahoteles.rest.dto;
/**
*
* @author administrador
*/
public record DDisponibilidad(int numHabSimple, int numHabDoble) {
}
...@@ -106,4 +106,12 @@ public class Mapeador { ...@@ -106,4 +106,12 @@ public class Mapeador {
dReserva.numHabSimple(), dReserva.numHabSimple(),
dReserva.numHabDoble()); dReserva.numHabDoble());
} }
public DDisponibilidad dto(Hotel.Disponibilidad disponibilidad) {
return new DDisponibilidad(disponibilidad.numHabSimple(), disponibilidad.numHabDoble());
}
public Hotel.Disponibilidad objetoValor(DDisponibilidad disponibilidad) {
return new Hotel.Disponibilidad(disponibilidad.numHabSimple(), disponibilidad.numHabDoble());
}
} }
...@@ -71,6 +71,10 @@ public class ServicioReservas { ...@@ -71,6 +71,10 @@ public class ServicioReservas {
return repositorioClientes.buscar(email).filter(cliente -> cliente.clave().equals(clave)); return repositorioClientes.buscar(email).filter(cliente -> cliente.clave().equals(clave));
} }
public Optional<Usuario> buscarUsuario(@Email String email) {
return repositorioClientes.buscar(email);
}
/** /**
* Búsqueda de hoteles disponibles en una localidad * Búsqueda de hoteles disponibles en una localidad
* @param localidad el nombre total o parcial de la localidad * @param localidad el nombre total o parcial de la localidad
...@@ -105,6 +109,15 @@ public class ServicioReservas { ...@@ -105,6 +109,15 @@ public class ServicioReservas {
} }
/** /**
* Búsqueda de hoteles por id
* @param id el nombre del hotel
* @return El hotel solicitado
*/
public Optional<Hotel> buscarHotel(int id) {
return repositorioHoteles.buscarPorId(id);
}
/**
* Carga las reservas de un hotel * Carga las reservas de un hotel
* @param hotel el hotel cuyas lista de reservas se va a cargar * @param hotel el hotel cuyas lista de reservas se va a cargar
* @return el hotel con las reservas * @return el hotel con las reservas
...@@ -113,31 +126,26 @@ public class ServicioReservas { ...@@ -113,31 +126,26 @@ public class ServicioReservas {
public Hotel hotelConReservas(Hotel hotel) { public Hotel hotelConReservas(Hotel hotel) {
hotel = repositorioHoteles.actualizar(hotel); hotel = repositorioHoteles.actualizar(hotel);
// Usar cualquier operación que acceda a las reservas para que se carguen // Usar cualquier operación que acceda a las reservas para que se carguen
hotel.disponible(LocalDate.now(), 1, 1); hotel.disponibilidad(LocalDate.now());
return hotel; return hotel;
} }
/** /**
* Comprueba si hay disponibilidad en las fechas indicadas * Devuelve la disponibilidad en las fechas indicadas
* @param hotel hotel donde se compreuba la disponibilidad * @param hotel hotel donde se compreuba la disponibilidad
* @param fechaInicio fecha de inicio de la reserva * @param fechaInicio fecha de inicio de la reserva
* @param fechaFin fecha de final de la reserva * @param fechaFin fecha de final de la reserva
* @param numHabSimple número de habitaciones simples solicitadas * @return la disponibilidad en las fechas indicadas
* @param numHabDoble número de habitaciones dobles solicitadas
* @return true si hay disponibilidad, false en caso contrario
*/ */
@Transactional @Transactional
public boolean disponible(Hotel hotel, public Hotel.Disponibilidad disponibilidad(Hotel hotel,
@FutureOrPresent LocalDate fechaInicio, @FutureOrPresent LocalDate fechaInicio,
@FutureOrPresent LocalDate fechaFin, @FutureOrPresent LocalDate fechaFin) {
@PositiveOrZero int numHabSimple,
@PositiveOrZero int numHabDoble) {
hotel = repositorioHoteles.actualizar(hotel); hotel = repositorioHoteles.actualizar(hotel);
return hotel.disponible(fechaInicio, fechaFin, numHabSimple, numHabDoble); return hotel.disponibilidad(fechaInicio, fechaFin);
} }
/** /**
* Realiza una reserva en un hotel. La reserva debe ser correcta y haber disponibilidad. * Realiza una reserva en un hotel. La reserva debe ser correcta y haber disponibilidad.
* *
......
package es.ujaen.dae.reservahoteles.rest; package es.ujaen.dae.reservahoteles.rest;
import es.ujaen.dae.reservahoteles.entidades.Usuario;
import es.ujaen.dae.reservahoteles.rest.dto.DHotel; import es.ujaen.dae.reservahoteles.rest.dto.DHotel;
import es.ujaen.dae.reservahoteles.rest.dto.DReserva;
import es.ujaen.dae.reservahoteles.rest.dto.DUsuario; import es.ujaen.dae.reservahoteles.rest.dto.DUsuario;
import jakarta.annotation.PostConstruct; import jakarta.annotation.PostConstruct;
import java.time.LocalDate;
import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThat;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
import org.springframework.boot.test.context.SpringBootTest; import org.springframework.boot.test.context.SpringBootTest;
...@@ -113,14 +116,6 @@ public class TestControladorReservas { ...@@ -113,14 +116,6 @@ public class TestControladorReservas {
var hotel1 = new DHotel(0, "Gran Hotel Almería", "Almería", "Almería", "04001", 25, 50, 100, 180); var hotel1 = new DHotel(0, "Gran Hotel Almería", "Almería", "Almería", "04001", 25, 50, 100, 180);
var hotel2 = new DHotel(0, "Hotel Infanta Cristina", "Jaén", "Jaén", "23009", 30, 60, 120, 200); var hotel2 = new DHotel(0, "Hotel Infanta Cristina", "Jaén", "Jaén", "23009", 30, 60, 120, 200);
var respuestaLogin = restTemplate.getForEntity(
"/usuarios/{email}?clave={clave}",
DUsuario.class,
"direccion@hotelxyz.es",
"SeCrEtO"
);
assertThat(respuestaLogin.getStatusCode()).isEqualTo(HttpStatus.OK);
var respuesta = restTemplate.postForEntity( var respuesta = restTemplate.postForEntity(
"/hoteles", "/hoteles",
hotel1, hotel1,
...@@ -145,4 +140,85 @@ public class TestControladorReservas { ...@@ -145,4 +140,85 @@ public class TestControladorReservas {
assertThat(respuestaConsulta.getBody()).hasSize(1); assertThat(respuestaConsulta.getBody()).hasSize(1);
assertThat(respuestaConsulta.getBody()[0].id()).isEqualTo(1); assertThat(respuestaConsulta.getBody()[0].id()).isEqualTo(1);
} }
@Test
@DirtiesContext
void testBuscarPorLocalidad() {
var hotel1 = new DHotel(0, "Gran Hotel Almería", "Almería", "Almería", "04001", 25, 50, 100, 180);
var hotel2 = new DHotel(0, "Hotel Espejo del Mar", "Almería", "Almería", "04001", 15, 35, 80, 110);
var hotel3 = new DHotel(0, "Hotel Infanta Cristina", "Jaén", "Jaén", "23009", 30, 60, 120, 200);
restTemplate.postForEntity(
"/hoteles",
hotel1,
DHotel.class
);
restTemplate.postForEntity(
"/hoteles",
hotel2,
DHotel.class
);
restTemplate.postForEntity(
"/hoteles",
hotel3,
DHotel.class
);
var respuestaConsulta = restTemplate.getForEntity(
"/hoteles?localidad={localidad}"+
"&desde={desde}&hasta={hasta}" +
"&numHabSimple={numHabSimple}&numHabDoble={numHabDoble}",
DHotel[].class,
" almeria",
LocalDate.now().plusDays(7),
LocalDate.now().plusDays(9),
0, 2
);
assertThat(respuestaConsulta.getStatusCode()).isEqualTo(HttpStatus.OK);
assertThat(respuestaConsulta.getBody()).hasSize(2);
assertThat(respuestaConsulta.getBody()[0].id()).isEqualTo(1);
assertThat(respuestaConsulta.getBody()[1].id()).isEqualTo(2);
}
@Test
@DirtiesContext
void testReservaHotel() {
var hotel = new DHotel(0, "Bed and Breakfast Almería", "Almería", "Almería", "04001", 2, 2, 60, 100);
restTemplate.postForEntity(
"/hoteles",
hotel,
DHotel.class
);
var usuario = new DUsuario("Pedro", "Jaén Jaén", "611203025", "pjaen@gmail.com", "miClAvE");
restTemplate.postForEntity(
"/usuarios",
usuario,
Void.class
);
var hotelGuardado = restTemplate.getForEntity(
"/hoteles?nombre={nombre}&localidad={localidad}",
DHotel[].class,
"bed and breakfast",
" almeria"
).getBody()[0];
var reserva = new DReserva(0,
LocalDate.now().plusDays(7),
LocalDate.now().plusDays(10), 0, 1,
usuario.email());
var respuestaReserva = restTemplate.postForEntity(
"/hoteles/{id}/reservas",
reserva,
DReserva.class,
hotelGuardado.id()
);
assertThat(respuestaReserva.getStatusCode()).isEqualTo(HttpStatus.CREATED);
}
} }
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