From cec15f2767cfd5b21c354ca090005223e9257731 Mon Sep 17 00:00:00 2001 From: migudel <miguel.moras@estudiantes.uva.es> Date: Sat, 28 Dec 2024 23:30:13 +0100 Subject: [PATCH] =?UTF-8?q?Estado=20de=20cliente=20administrado=20por=20re?= =?UTF-8?q?servas,=20programaci=C3=B3n=20de=20actualizaci=C3=B3n=20peri?= =?UTF-8?q?=C3=B3dica?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- java/services/bookings/pom.xml | 3 +- .../uva/api/bookings/BookingsApplication.java | 2 + .../com/uva/api/bookings/api/UserApi.java | 24 +++++- .../api/bookings/config/MyScheduledTasks.java | 25 +++++++ .../controllers/BookingController.java | 6 +- .../models/external/users/ClientDTO.java | 14 ++++ .../models/external/users/ClientStatus.java | 5 ++ .../repositories/BookingRepository.java | 11 +++ .../api/bookings/services/BookingService.java | 74 ++++++++++++++++--- .../users/controllers/ClientController.java | 8 +- .../java/com/uva/api/users/models/Client.java | 12 +-- 11 files changed, 154 insertions(+), 30 deletions(-) create mode 100644 java/services/bookings/src/main/java/com/uva/api/bookings/config/MyScheduledTasks.java create mode 100644 java/services/bookings/src/main/java/com/uva/api/bookings/models/external/users/ClientDTO.java create mode 100644 java/services/bookings/src/main/java/com/uva/api/bookings/models/external/users/ClientStatus.java diff --git a/java/services/bookings/pom.xml b/java/services/bookings/pom.xml index 51c840b..2fa958d 100644 --- a/java/services/bookings/pom.xml +++ b/java/services/bookings/pom.xml @@ -5,7 +5,7 @@ <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> - <version>3.3.4</version> + <version>3.3.7</version> <relativePath/> <!-- lookup parent from repository --> </parent> <groupId>com.uva</groupId> @@ -61,7 +61,6 @@ <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> - <version>1.18.36</version> <scope>provided</scope> </dependency> </dependencies> diff --git a/java/services/bookings/src/main/java/com/uva/api/bookings/BookingsApplication.java b/java/services/bookings/src/main/java/com/uva/api/bookings/BookingsApplication.java index 41ff330..6d21798 100644 --- a/java/services/bookings/src/main/java/com/uva/api/bookings/BookingsApplication.java +++ b/java/services/bookings/src/main/java/com/uva/api/bookings/BookingsApplication.java @@ -2,8 +2,10 @@ package com.uva.api.bookings; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.scheduling.annotation.EnableScheduling; @SpringBootApplication +@EnableScheduling public class BookingsApplication { public static void main(String[] args) { diff --git a/java/services/bookings/src/main/java/com/uva/api/bookings/api/UserApi.java b/java/services/bookings/src/main/java/com/uva/api/bookings/api/UserApi.java index a17bf87..dc68715 100644 --- a/java/services/bookings/src/main/java/com/uva/api/bookings/api/UserApi.java +++ b/java/services/bookings/src/main/java/com/uva/api/bookings/api/UserApi.java @@ -1,5 +1,8 @@ package com.uva.api.bookings.api; +import java.util.HashMap; +import java.util.Map; + import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; import org.springframework.http.HttpStatus; @@ -8,6 +11,9 @@ import org.springframework.stereotype.Component; import org.springframework.web.client.HttpClientErrorException; import org.springframework.web.client.RestTemplate; +import com.uva.api.bookings.models.external.users.ClientDTO; +import com.uva.api.bookings.models.external.users.ClientStatus; + @Component public class UserApi { @@ -20,14 +26,14 @@ public class UserApi { @Value("${services.external.managers.url}") private String MANAGERS_API_URL; - public boolean existsClientById(int id) { + public ClientDTO findClientById(int id) { try { String url = CLIENTS_API_URL + "/{id}"; - ResponseEntity<Void> response = restTemplate.getForEntity(url, Void.class, id); - return response.getStatusCode() == HttpStatus.OK; + ClientDTO client = restTemplate.getForObject(url, ClientDTO.class, id); + return client; } catch (HttpClientErrorException ex) { if (ex.getStatusCode() == HttpStatus.NOT_FOUND) { - return false; + return null; } throw ex; } @@ -47,4 +53,14 @@ public class UserApi { } } + public void updateClientState(int id, ClientStatus state) { + String url = CLIENTS_API_URL + "/{id}"; + + Map<String, Object> body = new HashMap<>(); + System.out.println(state); + body.put("status", state); + + restTemplate.put(url, body, id); + } + } diff --git a/java/services/bookings/src/main/java/com/uva/api/bookings/config/MyScheduledTasks.java b/java/services/bookings/src/main/java/com/uva/api/bookings/config/MyScheduledTasks.java new file mode 100644 index 0000000..d289884 --- /dev/null +++ b/java/services/bookings/src/main/java/com/uva/api/bookings/config/MyScheduledTasks.java @@ -0,0 +1,25 @@ +package com.uva.api.bookings.config; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.scheduling.annotation.Scheduled; +import org.springframework.stereotype.Component; + +import com.uva.api.bookings.services.BookingService; + +@Component +public class MyScheduledTasks { + + @Autowired + private BookingService bookingService; + + @Scheduled(cron = "0 30 0 * * *") // Se ejecuta cada dÃa a medianoche + public void updateInactiveBookings() { + System.out.println( + "Iniciando proceso de actualizar comunicación de cambio de estado para usuarios cuyas reservas finalizaron el dia de hoy"); + long start = System.currentTimeMillis(); + long updatedUsers = bookingService.performDailyClientsStateUpdate(); + long time = System.currentTimeMillis() - start; + + System.out.println(updatedUsers + " clients updated in " + time + " ml"); + } +} \ No newline at end of file diff --git a/java/services/bookings/src/main/java/com/uva/api/bookings/controllers/BookingController.java b/java/services/bookings/src/main/java/com/uva/api/bookings/controllers/BookingController.java index 2a8967a..d0d965a 100644 --- a/java/services/bookings/src/main/java/com/uva/api/bookings/controllers/BookingController.java +++ b/java/services/bookings/src/main/java/com/uva/api/bookings/controllers/BookingController.java @@ -8,6 +8,8 @@ import com.uva.api.bookings.models.Booking; import com.uva.api.bookings.services.BookingService; import java.time.LocalDate; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestParam; @RestController @RequestMapping("/bookings") @@ -33,7 +35,7 @@ public class BookingController { return bookingService.createBooking(booking); } - @GetMapping("/{id}") + @GetMapping("/{id:\\d+}") public ResponseEntity<?> getBookingById(@PathVariable Integer id) { return bookingService.getBookingById(id); } @@ -46,7 +48,7 @@ public class BookingController { return bookingService.deleteBookings(hotelId, managerId, userId); } - @DeleteMapping("/{id}") + @DeleteMapping("/{id:\\d+}") public ResponseEntity<?> deleteBooking(@PathVariable Integer id) { return bookingService.deleteBooking(id); } diff --git a/java/services/bookings/src/main/java/com/uva/api/bookings/models/external/users/ClientDTO.java b/java/services/bookings/src/main/java/com/uva/api/bookings/models/external/users/ClientDTO.java new file mode 100644 index 0000000..4a5ea6f --- /dev/null +++ b/java/services/bookings/src/main/java/com/uva/api/bookings/models/external/users/ClientDTO.java @@ -0,0 +1,14 @@ +package com.uva.api.bookings.models.external.users; + +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; + +@Getter +@Setter +@NoArgsConstructor +@AllArgsConstructor +public class ClientDTO { + private ClientStatus status; +} \ No newline at end of file diff --git a/java/services/bookings/src/main/java/com/uva/api/bookings/models/external/users/ClientStatus.java b/java/services/bookings/src/main/java/com/uva/api/bookings/models/external/users/ClientStatus.java new file mode 100644 index 0000000..3df8494 --- /dev/null +++ b/java/services/bookings/src/main/java/com/uva/api/bookings/models/external/users/ClientStatus.java @@ -0,0 +1,5 @@ +package com.uva.api.bookings.models.external.users; + +public enum ClientStatus { + NO_BOOKINGS, WITH_ACTIVE_BOOKINGS, WITH_INACTIVE_BOOKINGS; +} diff --git a/java/services/bookings/src/main/java/com/uva/api/bookings/repositories/BookingRepository.java b/java/services/bookings/src/main/java/com/uva/api/bookings/repositories/BookingRepository.java index 111ede8..c55c0d6 100644 --- a/java/services/bookings/src/main/java/com/uva/api/bookings/repositories/BookingRepository.java +++ b/java/services/bookings/src/main/java/com/uva/api/bookings/repositories/BookingRepository.java @@ -41,9 +41,20 @@ public interface BookingRepository extends JpaRepository<Booking, Integer> { List<Booking> findAllByManagerId(int managerId); + @Query("SELECT EXISTS (Select b from Booking b where b.userId = ?1 AND b.end >= ?2)") + boolean existsActiveByUserIdAndDate(int userId, LocalDate date); + + @Query("SELECT EXISTS (Select b from Booking b where b.userId = ?1 AND b.end < ?2)") + boolean existsInactiveByUserIdAndDate(int userId, LocalDate date); + @Transactional void deleteAllByUserId(int userId); @Transactional void deleteAllByManagerId(int managerId); + + @Query("SELECT b from Booking b where b.end = ?1") + List<Booking> findAllPassed(LocalDate yesterday); + // List<Booking> findAllByEnd(LocalDate end);// también puede ser + } diff --git a/java/services/bookings/src/main/java/com/uva/api/bookings/services/BookingService.java b/java/services/bookings/src/main/java/com/uva/api/bookings/services/BookingService.java index 7a3f8a4..7576034 100644 --- a/java/services/bookings/src/main/java/com/uva/api/bookings/services/BookingService.java +++ b/java/services/bookings/src/main/java/com/uva/api/bookings/services/BookingService.java @@ -11,11 +11,14 @@ import com.uva.api.bookings.api.UserApi; import com.uva.api.bookings.exceptions.BookingNotFoundException; import com.uva.api.bookings.exceptions.InvalidDateRangeException; import com.uva.api.bookings.models.Booking; +import com.uva.api.bookings.models.external.users.ClientDTO; +import com.uva.api.bookings.models.external.users.ClientStatus; import com.uva.api.bookings.repositories.BookingRepository; import java.time.LocalDate; import java.util.ArrayList; import java.util.List; +import java.util.stream.Stream; @Service public class BookingService { @@ -101,29 +104,37 @@ public class BookingService { public ResponseEntity<Booking> createBooking(Booking booking) { if (booking.getId() != null) booking.setId(null); + + if (booking.getStart().isAfter(booking.getEnd())) + throw new HttpClientErrorException(HttpStatus.BAD_REQUEST, + "La reserva no puede acabar antes de que empiece"); + int userId = booking.getUserId(); int roomId = booking.getRoomId(); int hotelId = booking.getHotelId(); int managerId = booking.getManagerId(); - // Check if the customer and rooms exists + List<Booking> existingBookings = bookingRepository.findAllByRoomIdInDateRange(roomId, booking.getStart(), + booking.getEnd()); + + // Local checks first + if (!existingBookings.isEmpty()) + throw new HttpClientErrorException(HttpStatus.BAD_REQUEST, + "Room is not available for the selected dates"); + if (!userApi.existsManagerById(managerId)) throw new HttpClientErrorException(HttpStatus.BAD_REQUEST, "Manager not found"); - if (!userApi.existsClientById(userId)) - throw new HttpClientErrorException(HttpStatus.BAD_REQUEST, "User not found"); - if (!hotelApi.existsById(hotelId, roomId)) throw new HttpClientErrorException(HttpStatus.BAD_REQUEST, "Hotel or room not found"); - // Check availability - List<Booking> existingBookings = bookingRepository.findAllByRoomIdInDateRange(roomId, booking.getStart(), - booking.getEnd()); + ClientDTO client = userApi.findClientById(userId); - if (!existingBookings.isEmpty()) { - throw new HttpClientErrorException(HttpStatus.BAD_REQUEST, - "Room is not available for the selected dates"); - } + if (client == null) + throw new HttpClientErrorException(HttpStatus.BAD_REQUEST, "User not found"); + + if (client.getStatus() != ClientStatus.WITH_ACTIVE_BOOKINGS) + userApi.updateClientState(userId, ClientStatus.WITH_ACTIVE_BOOKINGS); booking = bookingRepository.save(booking); @@ -141,9 +152,36 @@ public class BookingService { return ResponseEntity.ok(booking); } + private ClientStatus calculateClientStatus(int id) { + return calculateClientStatus(id, null); + } + + private ClientStatus calculateClientStatus(int id, LocalDate date) { + + date = date != null ? date : LocalDate.now(); + + boolean hasActiveBookings = bookingRepository.existsActiveByUserIdAndDate(id, date); + boolean hasInactiveBookings = bookingRepository.existsInactiveByUserIdAndDate(id, date); + + ClientStatus status; + if (hasActiveBookings) { + status = ClientStatus.WITH_ACTIVE_BOOKINGS; + } else if (hasInactiveBookings) { + status = ClientStatus.WITH_INACTIVE_BOOKINGS; + } else { + status = ClientStatus.NO_BOOKINGS; + } + return status; + } + public ResponseEntity<?> deleteBooking(Integer id) { Booking booking = findById(id); bookingRepository.deleteById(id); + + ClientStatus status = calculateClientStatus(id); + // In this case, the check if the client has already de state is expensive + userApi.updateClientState(id, status); + return ResponseEntity.ok(booking); } @@ -196,4 +234,18 @@ public class BookingService { } return ResponseEntity.ok(bookings); } + + public long performDailyClientsStateUpdate() { + LocalDate yesterday = LocalDate.now().minusDays(1); + + List<Booking> passedBookings = bookingRepository.findAllPassed(yesterday); + + Stream<Integer> userIds = passedBookings.stream().map(b -> b.getUserId()).distinct(); + userIds.forEach(userId -> { + ClientStatus status = calculateClientStatus(userId); + userApi.updateClientState(userId, status); + }); + + return userIds.count(); + } } diff --git a/java/services/users/src/main/java/com/uva/api/users/controllers/ClientController.java b/java/services/users/src/main/java/com/uva/api/users/controllers/ClientController.java index 8342e85..b9ee383 100644 --- a/java/services/users/src/main/java/com/uva/api/users/controllers/ClientController.java +++ b/java/services/users/src/main/java/com/uva/api/users/controllers/ClientController.java @@ -10,6 +10,7 @@ import org.springframework.web.bind.annotation.DeleteMapping; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PatchMapping; import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.PutMapping; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestHeader; import org.springframework.web.bind.annotation.RequestMapping; @@ -42,8 +43,13 @@ public class ClientController { } @PatchMapping("/{id:\\d+}") - public ResponseEntity<?> updateClientState(@PathVariable int id, @RequestBody Map<String, String> json) { + public ResponseEntity<?> updateClientStateWrapper(@PathVariable int id, @RequestBody Map<String, String> json) { + return updateClientState(id, json); + } + @PutMapping("/{id:\\d+}") + public ResponseEntity<?> updateClientState(@PathVariable int id, @RequestBody Map<String, String> json) { + json.entrySet().forEach(t -> System.out.println(t)); String strStatus = json.get("status"); if (strStatus == null) throw new HttpClientErrorException(HttpStatus.BAD_REQUEST, "Missing required fields"); diff --git a/java/services/users/src/main/java/com/uva/api/users/models/Client.java b/java/services/users/src/main/java/com/uva/api/users/models/Client.java index e28cca3..12ab1ed 100644 --- a/java/services/users/src/main/java/com/uva/api/users/models/Client.java +++ b/java/services/users/src/main/java/com/uva/api/users/models/Client.java @@ -1,5 +1,7 @@ package com.uva.api.users.models; +import java.time.LocalDate; + import jakarta.persistence.Column; import jakarta.persistence.Entity; import jakarta.persistence.EnumType; @@ -30,14 +32,4 @@ public class Client extends User { super(id, name, email, password, UserRol.CLIENT); setStatus(status); } - - // public ClientStatus getStatus() { - // if (getBookings() == null || getBookings().isEmpty()) - // return ClientStatus.NO_BOOKINGS; - // boolean activeBookings = getBookings().stream() - // .anyMatch(booking -> !booking.getEndDate().isBefore(LocalDate.now())); // - // reserva >= ahora - // return activeBookings ? ClientStatus.WITH_ACTIVE_BOOKINGS : - // ClientStatus.WITH_INACTIVE_BOOKINGS; - // } } -- GitLab