10 Reservation Service Implementation
10 Reservation Service Implementation
Goal
- Implement the guest reservation function.
User Reservation Implementation
Model Creation
- Open your project in Intellij, go to the
com.eve.staybooking.model
package and create a new class called Reservation.
- Add some private fields and the corresponding getters/setters/builder pattern. Remember to add JSON-related annotations.
package com.eve.staybooking.model;
import java.time.LocalDate;
public class Reservation {
private Long id;
private LocalDate checkinDate;
private LocalDate checkoutDate;
private User guest;
private Stay stay;
public Reservation() {}
private Reservation(Builder builder) {
this.id = builder.id;
this.checkinDate = builder.checkinDate;
this.checkoutDate = builder.checkoutDate;
this.guest = builder.guest;
this.stay = builder.stay;
}
public Long getId() {
return id;
}
public LocalDate getCheckinDate() {
return checkinDate;
}
public LocalDate getCheckoutDate() {
return checkoutDate;
}
public User getGuest() {
return guest;
}
public Reservation setGuest(User guest) {
this.guest = guest;
return this;
}
public Stay getStay() {
return stay;
}
public static class Builder {
private Long id;
private LocalDate checkinDate;
private LocalDate checkoutDate;
private User guest;
private Stay stay;
public Builder setId(Long id) {
this.id = id;
return this;
}
public Builder setCheckinDate(LocalDate checkinDate) {
this.checkinDate = checkinDate;
return this;
}
public Builder setCheckoutDate(LocalDate checkoutDate) {
this.checkoutDate = checkoutDate;
return this;
}
public Builder setGuest(User guest) {
this.guest = guest;
return this;
}
public Builder setStay(Stay stay) {
this.stay = stay;
return this;
}
public Reservation build() {
return new Reservation(this);
}
}
}
- Add Jackson and JPA related annotations.
package com.eve.staybooking.model;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
import javax.persistence.*;
import java.io.Serializable;
import java.time.LocalDate;
@Entity
@Table(name = "reservation")
@JsonDeserialize(builder = Reservation.Builder.class)
public class Reservation implements Serializable {
private static final long serialVersionUID = 1L;
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
@JsonProperty("checkin_date")
private LocalDate checkinDate;
@JsonProperty("checkout_date")
private LocalDate checkoutDate;
@ManyToOne
@JoinColumn(name = "user_id")
private User guest;
@ManyToOne
@JoinColumn(name = "stay_id")
private Stay stay;
public Reservation() {}
private Reservation(Builder builder) {
this.id = builder.id;
this.checkinDate = builder.checkinDate;
this.checkoutDate = builder.checkoutDate;
this.guest = builder.guest;
this.stay = builder.stay;
}
public Long getId() {
return id;
}
public LocalDate getCheckinDate() {
return checkinDate;
}
public LocalDate getCheckoutDate() {
return checkoutDate;
}
public User getGuest() {
return guest;
}
public Reservation setGuest(User guest) {
this.guest = guest;
return this;
}
public Stay getStay() {
return stay;
}
public static class Builder {
@JsonProperty("id")
private Long id;
@JsonProperty("checkin_date")
private LocalDate checkinDate;
@JsonProperty("checkout_date")
private LocalDate checkoutDate;
@JsonProperty("guest")
private User guest;
@JsonProperty("stay")
private Stay stay;
public Builder setId(Long id) {
this.id = id;
return this;
}
public Builder setCheckinDate(LocalDate checkinDate) {
this.checkinDate = checkinDate;
return this;
}
public Builder setCheckoutDate(LocalDate checkoutDate) {
this.checkoutDate = checkoutDate;
return this;
}
public Builder setGuest(User guest) {
this.guest = guest;
return this;
}
public Builder setStay(Stay stay) {
this.stay = stay;
return this;
}
public Reservation build() {
return new Reservation(this);
}
}
}
Create Reservation Repository
- Go to
com.eve.staybooking.repository
package and create ReservationRepository
package com.eve.staybooking.repository;
import com.eve.staybooking.model.Reservation;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;
@Repository
public interface ReservationRepository extends JpaRepository<Reservation, Long> {
}
- Add a couple of methods to support list by stay and list by guest functions.
@Repository
public interface ReservationRepository extends JpaRepository<Reservation, Long> {
List<Reservation> findByGuest(User guest);
List<Reservation> findByStay(Stay stay);
Reservation findByIdAndGuest(Long id, User guest); // for deletion
}
Implement Reservation Service
- Go to
com.eve.staybooking.exception
package and create ReservationCollisionException.
package com.eve.staybooking.exception;
public class ReservationCollisionException extends RuntimeException {
public ReservationCollisionException(String message) {
super(message);
}
}
- Under the same package, create another exception class ReservationNotFoundException.
package com.eve.staybooking.exception;
public class ReservationNotFoundException extends RuntimeException {
public ReservationNotFoundException(String message) {
super(message);
}
}
- Go to
com.eve.staybooking.service
package and create ReservationService.
package com.eve.staybooking.service;
import org.springframework.stereotype.Service;
@Service
public class ReservationService {
}
- Add ReservationRepository and StayReservationDateRepository as the private field.
package com.eve.staybooking.service;
import com.eve.staybooking.repository.ReservationRepository;
import com.eve.staybooking.repository.StayReservationDateRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
@Service
public class ReservationService {
private ReservationRepository reservationRepository;
private StayReservationDateRepository stayReservationDateRepository;
@Autowired
public ReservationService(ReservationRepository reservationRepository, StayReservationDateRepository stayReservationDateRepository) {
this.reservationRepository = reservationRepository;
this.stayReservationDateRepository = stayReservationDateRepository;
}
}
- Implement list, save and delete related functions.
package com.eve.staybooking.service;
import com.eve.staybooking.exception.ReservationCollisionException;
import com.eve.staybooking.exception.ReservationNotFoundException;
import com.eve.staybooking.model.*;
import com.eve.staybooking.repository.ReservationRepository;
import com.eve.staybooking.repository.StayReservationDateRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Isolation;
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.Set;
@Service
public class ReservationService {
private ReservationRepository reservationRepository;
private StayReservationDateRepository stayReservationDateRepository;
@Autowired
public ReservationService(ReservationRepository reservationRepository, StayReservationDateRepository stayReservationDateRepository) {
this.reservationRepository = reservationRepository;
this.stayReservationDateRepository = stayReservationDateRepository;
}
public List<Reservation> listByGuest(String username) {
return reservationRepository.findByGuest(new User.Builder().setUsername(username).build());
}
public List<Reservation> listByStay(Long stayId) {
return reservationRepository.findByStay(new Stay.Builder().setId(stayId).build());
}
@Transactional(isolation = Isolation.SERIALIZABLE)
public void add(Reservation reservation) throws ReservationCollisionException {
Set<Long> stayIds = stayReservationDateRepository.findByIdInAndDateBetween(Arrays.asList(reservation.getStay().getId()), reservation.getCheckinDate(), reservation.getCheckoutDate().minusDays(1));
if (!stayIds.isEmpty()) {
throw new ReservationCollisionException("Duplicate reservation");
}
List<StayReservedDate> reservedDates = new ArrayList<>();
for (LocalDate date = reservation.getCheckinDate(); date.isBefore(reservation.getCheckoutDate()); date = date.plusDays(1)) {
reservedDates.add(new StayReservedDate(new StayReservedDateKey(reservation.getStay().getId(), date), reservation.getStay()));
}
stayReservationDateRepository.saveAll(reservedDates);
reservationRepository.save(reservation);
}
@Transactional(isolation = Isolation.SERIALIZABLE)
public void delete(Long reservationId, String username) {
Reservation reservation = reservationRepository.findByIdAndGuest(reservationId, new User.Builder().setUsername(username).build());
if (reservation == null) {
throw new ReservationNotFoundException("Reservation is not available");
}
for (LocalDate date = reservation.getCheckinDate(); date.isBefore(reservation.getCheckoutDate()); date = date.plusDays(1)) {
stayReservationDateRepository.deleteById(new StayReservedDateKey(reservation.getStay().getId(), date));
}
reservationRepository.deleteById(reservationId);
}
}
Stay Deletion Service Update
- Since we support reservations now, we need to check active reservations before deleting a stay. Go to the ReservationRepository interface and add a new method.
package com.eve.staybooking.repository;
import com.eve.staybooking.model.Reservation;
import com.eve.staybooking.model.Stay;
import com.eve.staybooking.model.User;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;
import java.util.List;
@Repository
public interface ReservationRepository extends JpaRepository<Reservation, Long> {
List<Reservation> findByGuest(User guest);
List<Reservation> findByStay(Stay stay);
Reservation findByIdAndGuest(Long id, User guest); // for deletion
List<Reservation> findByStayAndCheckoutDateAfter(Stay stay, LocalDate date);
}
- Go to
com.eve.staybooking.exception
package and create StayDeleteException.
package com.eve.staybooking.exception;
public class StayDeleteException extends RuntimeException {
public StayDeleteException(String message) {
super(message);
}
}
- Go to the StayService class and update the delete() method.
package com.eve.staybooking.service;
import com.eve.staybooking.exception.StayDeleteException;
import com.eve.staybooking.exception.StayNotExistException;
import com.eve.staybooking.model.*;
import com.eve.staybooking.repository.LocationRepository;
import com.eve.staybooking.repository.ReservationRepository;
import com.eve.staybooking.repository.StayRepository;
import com.eve.staybooking.repository.StayReservationDateRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Isolation;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.multipart.MultipartFile;
import java.time.LocalDate;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;
@Service
public class StayService {
private StayRepository stayRepository;
private LocationRepository locationRepository;
private ImageStorageService imageStorageService;
private GeoCodingService geoCodingService;
private StayReservationDateRepository stayReservationDateRepository;
private ReservationRepository reservationRepository;
@Autowired
public StayService(StayRepository stayRepository, LocationRepository locationRepository, ReservationRepository reservationRepository, ImageStorageService imageStorageService, GeoCodingService geoCodingService, StayReservationDateRepository stayReservationDateRepository) {
this.stayRepository = stayRepository;
this.locationRepository = locationRepository;
this.reservationRepository = reservationRepository;
this.imageStorageService = imageStorageService;
this.geoCodingService = geoCodingService;
this.stayReservationDateRepository = stayReservationDateRepository;
}
public List<Stay> listByUser(String username) {
return stayRepository.findByHost(new User.Builder().setUsername(username).build());
}
public Stay findByIdAndHost(Long stayId, String username) throws StayNotExistException {
Stay stay = stayRepository.findByIdAndHost(stayId, new User.Builder().setUsername(username).build());
if (stay == null) {
throw new StayNotExistException("Stay doesn't exist");
}
return stay;
}
@Transactional(isolation = Isolation.SERIALIZABLE)
public void add(Stay stay, MultipartFile[] images) {
List<String> mediaLinks = Arrays.stream(images).parallel().map(image -> imageStorageService.save(image)).collect(Collectors.toList());
List<StayImage> stayImages = new ArrayList<>();
for (String mediaLink : mediaLinks) {
stayImages.add(new StayImage(mediaLink, stay));
}
stay.setImages(stayImages);
stayRepository.save(stay);
Location location = geoCodingService.getLatLng(stay.getId(), stay.getAddress());
locationRepository.save(location);
}
@Transactional(isolation = Isolation.SERIALIZABLE)
public void delete(Long stayId, String username) throws StayNotExistException, StayDeleteException {
Stay stay = stayRepository.findByIdAndHost(stayId, new User.Builder().setUsername(username).build());
if (stay == null) {
throw new StayNotExistException("Stay doesn't exist");
}
List<Reservation> reservations = reservationRepository.findByStayAndCheckoutDateAfter(stay, LocalDate.now());
if (reservations != null && reservations.size() > 0) {
throw new StayDeleteException("Cannot delete stay with active reservation");
}
stayRepository.deleteById(stayId);
}
}
Create Reservation Controller
- Under
com.eve.staybooking.exception
package, createInvalidReservationDateException.class
exception package.
package com.eve.staybooking.exception;
public class InvalidReservationDateException extends RuntimeException {
public InvalidReservationDateException(String message) {
super(message);
}
}
- Go to
com.eve.staybooking.controller
package and create a new ReservationController class.
package com.eve.staybooking.controller;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class ReservationController {
}
- Implement the reservation related APIs including list, add and delete.
package com.eve.staybooking.controller;
import com.eve.staybooking.exception.InvalidReservationDateException;
import com.eve.staybooking.model.Reservation;
import com.eve.staybooking.model.User;
import com.eve.staybooking.service.ReservationService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import java.security.Principal;
import java.time.LocalDate;
import java.util.List;
@RestController
public class ReservationController {
private ReservationService reservationService;
@Autowired
public ReservationController(ReservationService reservationService) {
this.reservationService = reservationService;
}
@GetMapping(value = "/reservations")
public List<Reservation> listReservations(Principal principal) {
return reservationService.listByGuest(principal.getName());
}
@PostMapping("/reservations")
public void addReservation(@RequestBody Reservation reservation, Principal principal) {
LocalDate checkinDate = reservation.getCheckinDate();
LocalDate checkoutDate = reservation.getCheckoutDate();
if (checkinDate.equals(checkoutDate) || checkinDate.isAfter(checkoutDate) || checkinDate.isBefore(LocalDate.now())) {
throw new InvalidReservationDateException("Invalid date for reservation");
}
reservation.setGuest(new User.Builder().setUsername(principal.getName()).build());
reservationService.add(reservation);
}
@DeleteMapping("/reservations/{reservationId}")
public void deleteReservation(@PathVariable Long reservationId, Principal principal) {
reservationService.delete(reservationId, principal.getName());
}
}
- Go to StayController and add a new API to support list reservation by stay function.
package com.eve.staybooking.controller;
import com.eve.staybooking.model.Reservation;
import com.eve.staybooking.model.Stay;
import com.eve.staybooking.model.User;
import com.eve.staybooking.service.ReservationService;
import com.eve.staybooking.service.StayService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;
import java.security.Principal;
import java.util.List;
@RestController
public class StayController {
private StayService stayService;
private ReservationService reservationService;
@Autowired
public StayController(StayService stayService, ReservationService reservationService) {
this.stayService = stayService;
this.reservationService = reservationService;
}
@GetMapping(value = "/stays")
public List<Stay> listStays(Principal principal) {
return stayService.listByUser(principal.getName());
}
@GetMapping(value = "/stays/{stayId}")
public Stay getStay(@PathVariable Long stayId, Principal principal) {
return stayService.findByIdAndHost(stayId, principal.getName());
}
@PostMapping("/stays")
public void addStay(
@RequestParam("name") String name,
@RequestParam("address") String address,
@RequestParam("description") String description,
@RequestParam("guest_number") int guestNumber,
@RequestParam("images") MultipartFile[] images, Principal principal) {
Stay stay = new Stay.Builder()
.setName(name)
.setAddress(address)
.setDescription(description)
.setGuestNumber(guestNumber)
.setHost(new User.Builder().setUsername(principal.getName()).build())
.build();
stayService.add(stay, images);
}
@DeleteMapping("/stays/{stayId}")
public void deleteStay(
@PathVariable Long stayId, Principal principal) {
stayService.delete(stayId, principal.getName());
}
@GetMapping(value = "/stays/reservations/{stayId}")
public List<Reservation> listReservations(@PathVariable Long stayId) {
return reservationService.listByStay(stayId);
}
}
- Go to CustomExceptionHandler class to include the exceptions you’ve added today.
@ControllerAdvice
public class CustomExceptionHandler {
...
@ExceptionHandler(ReservationCollisionException.class)
public final ResponseEntity<String> handleReservationCollisionExceptions(Exception ex, WebRequest request) {
return new ResponseEntity<>(ex.getMessage(), HttpStatus.CONFLICT);
}
@ExceptionHandler(InvalidReservationDateException.class)
public final ResponseEntity<String> handleInvalidReservationDateExceptions(Exception ex, WebRequest request) {
return new ResponseEntity<>(ex.getMessage(), HttpStatus.BAD_REQUEST);
}
@ExceptionHandler(ReservationNotFoundException.class)
public final ResponseEntity<String> handleReservationNotFoundExceptions(Exception ex, WebRequest request) {
return new ResponseEntity<>(ex.getMessage(), HttpStatus.NOT_FOUND);
}
@ExceptionHandler(StayDeleteException.class)
public final ResponseEntity<String> handleStayDeleteExceptions(Exception ex, WebRequest request) {
return new ResponseEntity<>(ex.getMessage(), HttpStatus.CONFLICT);
}
}
- Go to SecurityConfig class and add reservation URLs to the security config.
@Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.antMatchers(HttpMethod.POST, "/register/*").permitAll()
.antMatchers(HttpMethod.POST, "/authenticate/*").permitAll()
.antMatchers("/stays").hasAuthority("ROLE_HOST")
.antMatchers("/stays/*").hasAuthority("ROLE_HOST")
.antMatchers("/search").hasAuthority("ROLE_GUEST")
.antMatchers("/reservations").hasAuthority("ROLE_GUEST")
.antMatchers("/reservations/*").hasAuthority("ROLE_GUEST")
.anyRequest().authenticated()
.and()
.csrf()
.disable();
http
.sessionManagement()
.sessionCreationPolicy(SessionCreationPolicy.STATELESS)
.and()
.addFilterBefore(jwtFilter, UsernamePasswordAuthenticationFilter.class);
}
Test
- Save all your changes and start your project. Make sure there’s no error in the log.
- Open Postman and use the Guest Authentication request to log in as a guest user.
- Open the Stay Search request and under the Authorization tab, add the token generated from the last step.
- Send the request and make sure you can see the stay you uploaded previously.
- Open the Add Reservation request. Same as a search request, add the token generated from step 2
- Under the Body tab, replace the stay_id with the real ID you can find in step 4.
- Send the request and make sure there are no errors in the response.
- Open the List Reservations by Guest request, update the token and send the request. Make sure you can see the reservation you’ve just added
Stay.class
add@JsonIgnore
- Use the Host Authentication request to log in as a host.
-
Open the List Reservations by Stay request, update the token you’ve created in step 9.
-
Replace the ID in the URL with the real stay_id you can see in step 3. Send the request and make sure you can see the reservation you’ve just created as well.
-
Open the Delete Stay request, fill in the token created in step 9 and replace the stay_id with the same stay id you use in the last step. Send the request. Make sure you can see the 409 error in the response.
-
Open the Guest Authentication request and log in as a guest user again.
-
Open the Delete Reservation request and fill in the token returned in step 13.
-
Replace the ID with the real reservation id returned in step 8, send the request and make sure there’s no error in the response.
bug here
can not delete, but delete from database in hand
ReservationRepository
:package com.eve.staybooking.repository; import com.eve.staybooking.model.Reservation; import com.eve.staybooking.model.Stay; import com.eve.staybooking.model.User; import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.data.jpa.repository.Query; import org.springframework.stereotype.Repository; import java.time.LocalDate; import java.util.List; import java.util.Set; @Repository public interface ReservationRepository extends JpaRepository<Reservation, Long> { List<Reservation> findByGuest(User guest); List<Reservation> findByStay(Stay stay); Reservation findByIdAndGuest(Long id, User guest); // for deletion List<Reservation> findByStayAndCheckoutDateAfter(Stay stay, LocalDate date); @Query(value = "SELECT DISTINCT res.stay.id FROM Reservation res " + "WHERE res.stay.id IN ?1 AND " + "((res.checkinDate BETWEEN ?2 AND ?3) OR" + "(res.checkoutDate BETWEEN ?2 AND ?3))") // find the reservation already placed Set<Long> findByIdInAndDateBetween(List<Long> stayIds, LocalDate startDate, LocalDate endDate); }
ReservationsService.java
:package com.eve.staybooking.service; import com.eve.staybooking.exception.ReservationCollisionException; import com.eve.staybooking.exception.ReservationNotFoundException; import com.eve.staybooking.model.*; import com.eve.staybooking.repository.ReservationRepository; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Isolation; import org.springframework.transaction.annotation.Transactional; import java.util.Arrays; import java.util.List; import java.util.Set; @Service public class ReservationService { private ReservationRepository reservationRepository; @Autowired public ReservationService(ReservationRepository reservationRepository) { this.reservationRepository = reservationRepository; } public List<Reservation> listByGuest(String username) { return reservationRepository.findByGuest(new User.Builder().setUsername(username).build()); } public List<Reservation> listByStay(Long stayId) { return reservationRepository.findByStay(new Stay.Builder().setId(stayId).build()); } @Transactional(isolation = Isolation.SERIALIZABLE) public void add(Reservation reservation) throws ReservationCollisionException { Set<Long> stayIds = reservationRepository.findByIdInAndDateBetween(Arrays.asList(reservation.getStay().getId()), reservation.getCheckinDate(), reservation.getCheckoutDate().minusDays(1)); if (!stayIds.isEmpty()) { throw new ReservationCollisionException("Duplicate reservation"); } reservationRepository.save(reservation); } @Transactional(isolation = Isolation.SERIALIZABLE) public void delete(Long reservationId, String username) { Reservation reservation = reservationRepository.findByIdAndGuest(reservationId, new User.Builder().setUsername(username).build()); if (reservation == null) { throw new ReservationNotFoundException("Reservation is not available"); } reservationRepository.deleteById(reservationId); } }
SearchService.java
package com.eve.staybooking.service; import com.eve.staybooking.model.Stay; import com.eve.staybooking.repository.LocationRepository; import com.eve.staybooking.repository.ReservationRepository; import com.eve.staybooking.repository.StayRepository; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import java.time.LocalDate; import java.util.ArrayList; import java.util.List; import java.util.Set; @Service public class SearchService { private StayRepository stayRepository; private ReservationRepository reservationRepository; private LocationRepository locationRepository; @Autowired public SearchService(StayRepository stayRepository, LocationRepository locationRepository, ReservationRepository reservationRepository) { this.stayRepository = stayRepository; this.reservationRepository = reservationRepository; this.locationRepository = locationRepository; } public List<Stay> search(int guestNumber, LocalDate checkinDate, LocalDate checkoutDate, double lat, double lon, String distance) { List<Long> stayIds = locationRepository.searchByDistance(lat, lon, distance); if (stayIds == null || stayIds.isEmpty()) { return new ArrayList<>(); } Set<Long> reservedStayIds = reservationRepository.findByIdInAndDateBetween(stayIds, checkinDate, checkoutDate.minusDays(1)); List<Long> filteredStayIds = new ArrayList<>(); for (Long stayId : stayIds) { if (!reservedStayIds.contains(stayId)) { filteredStayIds.add(stayId); } } return stayRepository.findByIdInAndGuestNumberGreaterThanEqual(stayIds, guestNumber); } }
StayService.java
package com.eve.staybooking.service; import com.eve.staybooking.exception.StayDeleteException; import com.eve.staybooking.exception.StayNotExistException; import com.eve.staybooking.model.*; import com.eve.staybooking.repository.LocationRepository; import com.eve.staybooking.repository.ReservationRepository; import com.eve.staybooking.repository.StayRepository; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Isolation; import org.springframework.transaction.annotation.Transactional; import org.springframework.web.multipart.MultipartFile; import java.time.LocalDate; import java.util.ArrayList; import java.util.Arrays; import java.util.List; import java.util.stream.Collectors; @Service public class StayService { private StayRepository stayRepository; private LocationRepository locationRepository; private ImageStorageService imageStorageService; private GeoCodingService geoCodingService; private ReservationRepository reservationRepository; @Autowired public StayService(StayRepository stayRepository, LocationRepository locationRepository, ReservationRepository reservationRepository, ImageStorageService imageStorageService, GeoCodingService geoCodingService) { this.stayRepository = stayRepository; this.locationRepository = locationRepository; this.reservationRepository = reservationRepository; this.imageStorageService = imageStorageService; this.geoCodingService = geoCodingService; } public List<Stay> listByUser(String username) { return stayRepository.findByHost(new User.Builder().setUsername(username).build()); } public Stay findByIdAndHost(Long stayId, String username) throws StayNotExistException { Stay stay = stayRepository.findByIdAndHost(stayId, new User.Builder().setUsername(username).build()); if (stay == null) { throw new StayNotExistException("Stay doesn't exist"); } return stay; } @Transactional(isolation = Isolation.SERIALIZABLE) public void add(Stay stay, MultipartFile[] images) { List<String> mediaLinks = Arrays.stream(images).parallel().map(image -> imageStorageService.save(image)).collect(Collectors.toList()); // Arrays.stream.parallel()是一个创建并行操作(multi-thread)的方法, java8后台实现 List<StayImage> stayImages = new ArrayList<>(); for (String mediaLink : mediaLinks) { stayImages.add(new StayImage(mediaLink, stay)); } stay.setImages(stayImages); stayRepository.save(stay); Location location = geoCodingService.getLatLng(stay.getId(), stay.getAddress()); locationRepository.save(location); } @Transactional(isolation = Isolation.SERIALIZABLE) public void delete(Long stayId, String username) throws StayNotExistException, StayDeleteException { Stay stay = stayRepository.findByIdAndHost(stayId, new User.Builder().setUsername(username).build()); if (stay == null) { throw new StayNotExistException("Stay doesn't exist"); } List<Reservation> reservations = reservationRepository.findByStayAndCheckoutDateAfter(stay, LocalDate.now()); if (reservations != null && reservations.size() > 0) { throw new StayDeleteException("Cannot delete stay with active reservation"); } stayRepository.deleteById(stayId); } }
- Open the List Reservations by Guest request, update the token and send the request. Now there should be no reservation in the response.