From bee1c9b9602f04c8f8c884dbbce27b23689dae18 Mon Sep 17 00:00:00 2001 From: AntonBabychP1T Date: Sun, 14 Jan 2024 13:39:56 +0200 Subject: [PATCH] [API] Add RentalController. Add Rental model, RentalService and other required components. --- .../controller/RentalController.java | 50 +++++++++++ .../dto/rental/RentalRequestDto.java | 16 ++++ .../dto/rental/RentalResponseDto.java | 12 +++ .../carsharing/mapper/RentalMapper.java | 19 ++++ .../java/service/carsharing/model/Rental.java | 47 ++++++++++ .../repository/RentalRepository.java | 12 +++ .../carsharing/service/RentalService.java | 15 ++++ .../service/impl/RentalServiceImpl.java | 86 +++++++++++++++++++ .../changes/01-create-user-table.yaml | 1 + .../changes/06-create-cars-table.yaml | 1 + .../changes/07-create-rentals-table.yaml | 48 +++++++++++ .../db/changelog/db.changelog-master.yaml | 4 +- 12 files changed, 310 insertions(+), 1 deletion(-) create mode 100644 src/main/java/service/carsharing/controller/RentalController.java create mode 100644 src/main/java/service/carsharing/dto/rental/RentalRequestDto.java create mode 100644 src/main/java/service/carsharing/dto/rental/RentalResponseDto.java create mode 100644 src/main/java/service/carsharing/mapper/RentalMapper.java create mode 100644 src/main/java/service/carsharing/model/Rental.java create mode 100644 src/main/java/service/carsharing/repository/RentalRepository.java create mode 100644 src/main/java/service/carsharing/service/RentalService.java create mode 100644 src/main/java/service/carsharing/service/impl/RentalServiceImpl.java create mode 100644 src/main/resources/db/changelog/changes/07-create-rentals-table.yaml diff --git a/src/main/java/service/carsharing/controller/RentalController.java b/src/main/java/service/carsharing/controller/RentalController.java new file mode 100644 index 0000000..138cbac --- /dev/null +++ b/src/main/java/service/carsharing/controller/RentalController.java @@ -0,0 +1,50 @@ +package service.carsharing.controller; + +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.tags.Tag; +import jakarta.validation.Valid; +import java.util.List; +import lombok.RequiredArgsConstructor; +import org.springframework.security.core.Authentication; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; +import service.carsharing.dto.rental.RentalRequestDto; +import service.carsharing.dto.rental.RentalResponseDto; +import service.carsharing.service.RentalService; + +@Tag(name = "Rental managing", description = "Endpoint to rentals managing") +@RequiredArgsConstructor +@RestController +@RequestMapping("api/rentals") +public class RentalController { + private final RentalService rentalService; + + @PostMapping() + @Operation(summary = "Create a new rental", description = "Create a new rental") + public RentalResponseDto addRental(@RequestBody @Valid RentalRequestDto requestDto) { + return rentalService.addNewRental(requestDto); + } + + @GetMapping("/{userId}/{isActive}") + @Operation(summary = "Get all active rentals", + description = "Get list of all active rentals for specify user") + public List getAllActiveRentals(@PathVariable Long userId, + @PathVariable boolean isActive) { + return rentalService.getAllCurrentRentals(userId, isActive); + } + + @GetMapping("/{id}") + @Operation(summary = "Get rental by id", description = "Get specify rental by id") + public RentalResponseDto getRental(@PathVariable Long id, Authentication authentication) { + return rentalService.getRentalById(id, authentication.getName()); + } + + @PostMapping("/{id}/return") + public RentalResponseDto returnRental(@PathVariable Long id, Authentication authentication) { + return rentalService.returnRental(id, authentication.getName()); + } +} diff --git a/src/main/java/service/carsharing/dto/rental/RentalRequestDto.java b/src/main/java/service/carsharing/dto/rental/RentalRequestDto.java new file mode 100644 index 0000000..5410800 --- /dev/null +++ b/src/main/java/service/carsharing/dto/rental/RentalRequestDto.java @@ -0,0 +1,16 @@ +package service.carsharing.dto.rental; + +import jakarta.validation.constraints.NotNull; +import java.time.LocalDate; + +public record RentalRequestDto( + @NotNull + LocalDate rentalDate, + @NotNull + LocalDate returnDate, + @NotNull + Long carId, + @NotNull + Long userId +) { +} diff --git a/src/main/java/service/carsharing/dto/rental/RentalResponseDto.java b/src/main/java/service/carsharing/dto/rental/RentalResponseDto.java new file mode 100644 index 0000000..95880d7 --- /dev/null +++ b/src/main/java/service/carsharing/dto/rental/RentalResponseDto.java @@ -0,0 +1,12 @@ +package service.carsharing.dto.rental; + +import java.time.LocalDate; + +public record RentalResponseDto( + LocalDate rentalDate, + LocalDate returnDate, + LocalDate actualReturnDate, + Long carId, + Long userId +) { +} diff --git a/src/main/java/service/carsharing/mapper/RentalMapper.java b/src/main/java/service/carsharing/mapper/RentalMapper.java new file mode 100644 index 0000000..5b390d3 --- /dev/null +++ b/src/main/java/service/carsharing/mapper/RentalMapper.java @@ -0,0 +1,19 @@ +package service.carsharing.mapper; + +import org.mapstruct.Mapper; +import org.mapstruct.Mapping; +import service.carsharing.config.MapperConfig; +import service.carsharing.dto.rental.RentalRequestDto; +import service.carsharing.dto.rental.RentalResponseDto; +import service.carsharing.model.Rental; + +@Mapper(config = MapperConfig.class) +public interface RentalMapper { + @Mapping(target = "user.id", source = "userId") + @Mapping(target = "car.id", source = "carId") + Rental toModel(RentalRequestDto requestDto); + + @Mapping(target = "userId", source = "user.id") + @Mapping(target = "carId", source = "car.id") + RentalResponseDto toDto(Rental rental); +} diff --git a/src/main/java/service/carsharing/model/Rental.java b/src/main/java/service/carsharing/model/Rental.java new file mode 100644 index 0000000..7b84ee5 --- /dev/null +++ b/src/main/java/service/carsharing/model/Rental.java @@ -0,0 +1,47 @@ +package service.carsharing.model; + +import jakarta.persistence.Column; +import jakarta.persistence.Entity; +import jakarta.persistence.GeneratedValue; +import jakarta.persistence.GenerationType; +import jakarta.persistence.Id; +import jakarta.persistence.JoinColumn; +import jakarta.persistence.ManyToOne; +import jakarta.persistence.PreRemove; +import jakarta.persistence.Table; +import java.time.LocalDate; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.ToString; + +@Data +@Entity +@Table(name = "rentals") +public class Rental { + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private Long id; + @Column(nullable = false) + private LocalDate rentalDate; + @Column(nullable = false) + private LocalDate returnDate; + private LocalDate actualReturnDate; + @ToString.Exclude + @EqualsAndHashCode.Exclude + @ManyToOne + @JoinColumn(name = "car_id", nullable = false) + private Car car; + @ToString.Exclude + @EqualsAndHashCode.Exclude + @ManyToOne + @JoinColumn(name = "user_id", nullable = false) + private User user; + @Column(nullable = false) + private boolean deleted = false; + + @PreRemove + public void preRemove() { + this.deleted = true; + } + +} diff --git a/src/main/java/service/carsharing/repository/RentalRepository.java b/src/main/java/service/carsharing/repository/RentalRepository.java new file mode 100644 index 0000000..366df91 --- /dev/null +++ b/src/main/java/service/carsharing/repository/RentalRepository.java @@ -0,0 +1,12 @@ +package service.carsharing.repository; + +import java.util.List; +import java.util.Optional; +import org.springframework.data.jpa.repository.JpaRepository; +import service.carsharing.model.Rental; + +public interface RentalRepository extends JpaRepository { + Optional findByIdAndUserIdAndDeletedFalse(Long rentalId, Long userId); + + List getAllByUserIdAndDeletedFalse(Long id); +} diff --git a/src/main/java/service/carsharing/service/RentalService.java b/src/main/java/service/carsharing/service/RentalService.java new file mode 100644 index 0000000..c277d66 --- /dev/null +++ b/src/main/java/service/carsharing/service/RentalService.java @@ -0,0 +1,15 @@ +package service.carsharing.service; + +import java.util.List; +import service.carsharing.dto.rental.RentalRequestDto; +import service.carsharing.dto.rental.RentalResponseDto; + +public interface RentalService { + RentalResponseDto addNewRental(RentalRequestDto requestDto); + + List getAllCurrentRentals(Long userId, boolean isActive); + + RentalResponseDto getRentalById(Long id, String email); + + RentalResponseDto returnRental(Long rentalId, String email); +} diff --git a/src/main/java/service/carsharing/service/impl/RentalServiceImpl.java b/src/main/java/service/carsharing/service/impl/RentalServiceImpl.java new file mode 100644 index 0000000..03703c2 --- /dev/null +++ b/src/main/java/service/carsharing/service/impl/RentalServiceImpl.java @@ -0,0 +1,86 @@ +package service.carsharing.service.impl; + +import jakarta.persistence.EntityNotFoundException; +import java.time.LocalDate; +import java.util.List; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; +import service.carsharing.dto.rental.RentalRequestDto; +import service.carsharing.dto.rental.RentalResponseDto; +import service.carsharing.mapper.RentalMapper; +import service.carsharing.model.Car; +import service.carsharing.model.Rental; +import service.carsharing.model.User; +import service.carsharing.repository.CarRepository; +import service.carsharing.repository.RentalRepository; +import service.carsharing.repository.UserRepository; +import service.carsharing.service.RentalService; + +@RequiredArgsConstructor +@Service +public class RentalServiceImpl implements RentalService { + private final RentalRepository rentalRepository; + private final UserRepository userRepository; + private final CarRepository carRepository; + private final RentalMapper rentalMapper; + + @Override + @Transactional + public RentalResponseDto addNewRental(RentalRequestDto requestDto) { + Car car = getCarById(requestDto.carId()); + car.setInventory(car.getInventory() - 1); + carRepository.save(car); + return rentalMapper.toDto(rentalRepository.save(rentalMapper.toModel(requestDto))); + } + + @Override + public List getAllCurrentRentals(Long userId, boolean isActive) { + List allRentalsByUser = rentalRepository.getAllByUserIdAndDeletedFalse(userId); + if (isActive) { + return allRentalsByUser.stream() + .filter(rental -> rental.getActualReturnDate() == null) + .map(rentalMapper::toDto) + .toList(); + } + return allRentalsByUser.stream() + .filter(rental -> rental.getActualReturnDate() != null) + .map(rentalMapper::toDto) + .toList(); + } + + @Override + public RentalResponseDto getRentalById(Long id, String email) { + Rental rental = getRentalByIdAndUserId(id, getUserByEmail(email).getId()); + return rentalMapper.toDto(rental); + } + + @Override + @Transactional + public RentalResponseDto returnRental(Long rentalId, String email) { + Rental rental = getRentalByIdAndUserId(rentalId, getUserByEmail(email).getId()); + Car car = getCarById(rental.getCar().getId()); + car.setInventory(car.getInventory() + 1); + rental.setActualReturnDate(LocalDate.now()); + return rentalMapper.toDto(rentalRepository.save(rental)); + } + + private User getUserByEmail(String email) { + return userRepository.findByEmail(email).orElseThrow( + () -> new EntityNotFoundException("Can't find user with email: " + email) + ); + } + + private Rental getRentalByIdAndUserId(Long rentalId, Long userId) { + return rentalRepository + .findByIdAndUserIdAndDeletedFalse(rentalId, userId).orElseThrow( + () -> new EntityNotFoundException("Can't find rental with id: " + rentalId) + ); + } + + private Car getCarById(Long id) { + return carRepository.findByIdAndDeletedFalse(id).orElseThrow( + () -> new EntityNotFoundException("Can't find car with id: " + id) + ); + } +} diff --git a/src/main/resources/db/changelog/changes/01-create-user-table.yaml b/src/main/resources/db/changelog/changes/01-create-user-table.yaml index e94a2eb..d67f5b4 100644 --- a/src/main/resources/db/changelog/changes/01-create-user-table.yaml +++ b/src/main/resources/db/changelog/changes/01-create-user-table.yaml @@ -37,5 +37,6 @@ databaseChangeLog: - column: name: deleted type: boolean + value: 0 constraints: nullable: false diff --git a/src/main/resources/db/changelog/changes/06-create-cars-table.yaml b/src/main/resources/db/changelog/changes/06-create-cars-table.yaml index 2ed124c..b1d6fc8 100644 --- a/src/main/resources/db/changelog/changes/06-create-cars-table.yaml +++ b/src/main/resources/db/changelog/changes/06-create-cars-table.yaml @@ -41,5 +41,6 @@ databaseChangeLog: - column: name: deleted type: boolean + value: 0 constraints: nullable: false diff --git a/src/main/resources/db/changelog/changes/07-create-rentals-table.yaml b/src/main/resources/db/changelog/changes/07-create-rentals-table.yaml new file mode 100644 index 0000000..5cff752 --- /dev/null +++ b/src/main/resources/db/changelog/changes/07-create-rentals-table.yaml @@ -0,0 +1,48 @@ +databaseChangeLog: + - changeSet: + id: create-rentals-table + author: antonbabych + changes: + - createTable: + tableName: rentals + columns: + - column: + name: id + type: bigint + autoIncrement: true + constraints: + primaryKey: true + nullable: false + - column: + name: rental_date + type: date + constraints: + nullable: false + - column: + name: return_date + type: date + constraints: + nullable: false + - column: + name: actual_return_date + type: date + - column: + name: car_id + type: bigint + constraints: + nullable: false + foreignKeyName: fk_rentals_cars + references: cars(id) + - column: + name: user_id + type: bigint + constraints: + nullable: false + foreignKeyName: fk_rentals_users + references: users(id) + - column: + name: deleted + type: boolean + value: 0 + constraints: + nullable: false diff --git a/src/main/resources/db/changelog/db.changelog-master.yaml b/src/main/resources/db/changelog/db.changelog-master.yaml index 5ae4ef7..68bda1a 100644 --- a/src/main/resources/db/changelog/db.changelog-master.yaml +++ b/src/main/resources/db/changelog/db.changelog-master.yaml @@ -10,4 +10,6 @@ databaseChangeLog: - include: file: db/changelog/changes/05-create-default-manager-user.yaml - include: - file: db/changelog/changes/06-create-cars-table.yaml \ No newline at end of file + file: db/changelog/changes/06-create-cars-table.yaml + - include: + file: db/changelog/changes/07-create-rentals-table.yaml \ No newline at end of file