diff --git a/src/main/java/com/project/carsharingapp/controller/RentalController.java b/src/main/java/com/project/carsharingapp/controller/RentalController.java index 81e28ca..9147995 100644 --- a/src/main/java/com/project/carsharingapp/controller/RentalController.java +++ b/src/main/java/com/project/carsharingapp/controller/RentalController.java @@ -2,40 +2,46 @@ import com.project.carsharingapp.dto.rental.CreateRentalRequestDto; import com.project.carsharingapp.dto.rental.RentalDto; -import com.project.carsharingapp.dto.rental.SetActualReturnDateRequestDto; +import com.project.carsharingapp.service.rental.RentalService; 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.http.HttpStatus; 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.RequestParam; import org.springframework.web.bind.annotation.ResponseStatus; import org.springframework.web.bind.annotation.RestController; @Tag(name = "Rental management", description = "Endpoints for managing rentals") @RestController +@RequiredArgsConstructor @RequestMapping(value = "/rentals") public class RentalController { + private final RentalService rentalService; + @PostMapping @ResponseStatus(HttpStatus.CREATED) @Operation(summary = "Save new rental", description = "Save new rental and decrease car inventory by 1") public RentalDto add(@RequestBody @Valid CreateRentalRequestDto requestDto) { - return null; + return rentalService.add(requestDto); } - @GetMapping("/{userId}/{isActive}") + @GetMapping("/{user_id}/{is_active}") @ResponseStatus(HttpStatus.OK) @Operation(summary = "Get the rentals by user id and active status", description = "Retrieve rentals by user identification number" + " and whether the rental is still active or not") - public RentalDto getByUserIdAndActiveStatus(@PathVariable Long userId, - @PathVariable boolean isActive) { - return null; + public List getByUserIdAndActiveStatus(@RequestParam Long userId, + @RequestParam boolean isActive) { + return rentalService.getByUserIdAndActiveStatus(userId, isActive); } @GetMapping("/{id}") @@ -43,15 +49,14 @@ public RentalDto getByUserIdAndActiveStatus(@PathVariable Long userId, @Operation(summary = "Get the rental by id", description = "Get specific rental by identification number") public RentalDto getById(@PathVariable Long id) { - return null; + return rentalService.getById(id); } - @PostMapping("/{returnDate}") + @PostMapping("/return") @ResponseStatus(HttpStatus.OK) @Operation(summary = "Set actual return date", description = "Set actual return date and increase car inventory by 1") - public RentalDto setActualReturnDay(@RequestBody - @Valid SetActualReturnDateRequestDto returnDate) { - return null; + public RentalDto setActualReturnDay(Long id) { + return rentalService.setActualReturnDay(id); } } diff --git a/src/main/java/com/project/carsharingapp/controller/UserController.java b/src/main/java/com/project/carsharingapp/controller/UserController.java index 597fb15..7a77c8b 100644 --- a/src/main/java/com/project/carsharingapp/controller/UserController.java +++ b/src/main/java/com/project/carsharingapp/controller/UserController.java @@ -3,9 +3,11 @@ import com.project.carsharingapp.dto.user.UpdateUserProfileRequestDto; import com.project.carsharingapp.dto.user.UpdateUserRoleRequestDto; import com.project.carsharingapp.dto.user.UserDto; +import com.project.carsharingapp.service.UserService; import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.tags.Tag; import jakarta.validation.Valid; +import lombok.RequiredArgsConstructor; import org.springframework.http.HttpStatus; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PatchMapping; @@ -18,9 +20,11 @@ @Tag(name = "User management", description = "Endpoints for managing users") +@RequiredArgsConstructor @RestController @RequestMapping(value = "/users") public class UserController { + private final UserService userService; @GetMapping("/{id}") @ResponseStatus(HttpStatus.OK) @@ -28,7 +32,7 @@ public class UserController { description = "Get user's detailed information about" + " user profile by user identification number") public UserDto getById(@PathVariable Long id) { - return null; + return userService.findById(id); } @PatchMapping("/{id}/role") @@ -38,7 +42,7 @@ public UserDto getById(@PathVariable Long id) { public UserDto updateUserRole(@PathVariable Long id, @RequestBody @Valid UpdateUserRoleRequestDto requestDto) { - return null; + return userService.updateUserRole(id, requestDto); } @PutMapping("/{id}") @@ -48,6 +52,6 @@ public UserDto updateUserRole(@PathVariable Long id, + "by user identification number") public UserDto updateUserProfile(@PathVariable Long id, @Valid @RequestBody UpdateUserProfileRequestDto requestDto) { - return null; + return userService.updateUserProfile(id, requestDto); } } diff --git a/src/main/java/com/project/carsharingapp/dto/rental/CreateRentalRequestDto.java b/src/main/java/com/project/carsharingapp/dto/rental/CreateRentalRequestDto.java index 7a8af1a..608c310 100644 --- a/src/main/java/com/project/carsharingapp/dto/rental/CreateRentalRequestDto.java +++ b/src/main/java/com/project/carsharingapp/dto/rental/CreateRentalRequestDto.java @@ -8,14 +8,10 @@ @Getter @Setter public class CreateRentalRequestDto { - @NotNull private LocalDateTime rentalDate; @NotNull private LocalDateTime returnDate; @NotNull - private LocalDateTime actualReturnDate; - @NotNull private Long carId; - @NotNull - private Long userId; + } diff --git a/src/main/java/com/project/carsharingapp/dto/rental/RentalSearchParametersDto.java b/src/main/java/com/project/carsharingapp/dto/rental/RentalSearchParametersDto.java new file mode 100644 index 0000000..3480660 --- /dev/null +++ b/src/main/java/com/project/carsharingapp/dto/rental/RentalSearchParametersDto.java @@ -0,0 +1,7 @@ +package com.project.carsharingapp.dto.rental; + +public record RentalSearchParametersDto( + String[] userId, + String[] isActive +) { +} diff --git a/src/main/java/com/project/carsharingapp/dto/user/UpdateUserRoleRequestDto.java b/src/main/java/com/project/carsharingapp/dto/user/UpdateUserRoleRequestDto.java index 7c83deb..24006e7 100644 --- a/src/main/java/com/project/carsharingapp/dto/user/UpdateUserRoleRequestDto.java +++ b/src/main/java/com/project/carsharingapp/dto/user/UpdateUserRoleRequestDto.java @@ -1,6 +1,6 @@ package com.project.carsharingapp.dto.user; -import com.project.carsharingapp.model.RoleName; +import com.project.carsharingapp.model.Role; import jakarta.validation.constraints.NotNull; import lombok.Getter; import lombok.Setter; @@ -9,5 +9,5 @@ @Setter public class UpdateUserRoleRequestDto { @NotNull - private RoleName role; + private Role.RoleName role; } diff --git a/src/main/java/com/project/carsharingapp/mapper/RentalMapper.java b/src/main/java/com/project/carsharingapp/mapper/RentalMapper.java new file mode 100644 index 0000000..5e09808 --- /dev/null +++ b/src/main/java/com/project/carsharingapp/mapper/RentalMapper.java @@ -0,0 +1,17 @@ +package com.project.carsharingapp.mapper; + +import com.project.carsharingapp.config.MapperConfig; +import com.project.carsharingapp.dto.rental.CreateRentalRequestDto; +import com.project.carsharingapp.dto.rental.RentalDto; +import com.project.carsharingapp.model.Rental; +import org.mapstruct.Mapper; +import org.mapstruct.Mapping; + +@Mapper(config = MapperConfig.class) +public interface RentalMapper { + Rental toEntity(CreateRentalRequestDto requestDto); + + @Mapping(source = "user.id", target = "userId") + @Mapping(source = "car.id", target = "carId") + RentalDto toDto(Rental rental); +} diff --git a/src/main/java/com/project/carsharingapp/mapper/UserMapper.java b/src/main/java/com/project/carsharingapp/mapper/UserMapper.java new file mode 100644 index 0000000..8c7ee1f --- /dev/null +++ b/src/main/java/com/project/carsharingapp/mapper/UserMapper.java @@ -0,0 +1,11 @@ +package com.project.carsharingapp.mapper; + +import com.project.carsharingapp.config.MapperConfig; +import com.project.carsharingapp.dto.user.UserDto; +import com.project.carsharingapp.model.User; +import org.mapstruct.Mapper; + +@Mapper(config = MapperConfig.class) +public interface UserMapper { + UserDto toDto(User user); +} diff --git a/src/main/java/com/project/carsharingapp/model/Rental.java b/src/main/java/com/project/carsharingapp/model/Rental.java index 76117d3..60e4bff 100644 --- a/src/main/java/com/project/carsharingapp/model/Rental.java +++ b/src/main/java/com/project/carsharingapp/model/Rental.java @@ -26,18 +26,27 @@ public class Rental { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; + @Column(name = "rental_date", nullable = false) private LocalDateTime rentalDate; + @Column(name = "return_date", nullable = false) private LocalDateTime returnDate; + @Column(name = "actual_return_date") private LocalDateTime actualReturnDate; + @OneToOne(fetch = FetchType.LAZY) @JoinColumn(name = "car_id", nullable = false) private Car car; + @ManyToOne(fetch = FetchType.LAZY) @JoinColumn(name = "user_id", nullable = false) private User user; + @Column(name = "is_deleted",nullable = false) private boolean isDeleted; + + @Column(name = "is_active",nullable = false) + private boolean isActive = false; } diff --git a/src/main/java/com/project/carsharingapp/model/Role.java b/src/main/java/com/project/carsharingapp/model/Role.java index cd14e93..660e356 100644 --- a/src/main/java/com/project/carsharingapp/model/Role.java +++ b/src/main/java/com/project/carsharingapp/model/Role.java @@ -22,4 +22,9 @@ public class Role { @Column(name = "name", nullable = false, unique = true) @Enumerated(EnumType.STRING) private RoleName roleName; + + public enum RoleName { + ROLE_MANAGER, + ROLE_CUSTOMER + } } diff --git a/src/main/java/com/project/carsharingapp/model/RoleName.java b/src/main/java/com/project/carsharingapp/model/RoleName.java deleted file mode 100644 index ea6a3c7..0000000 --- a/src/main/java/com/project/carsharingapp/model/RoleName.java +++ /dev/null @@ -1,6 +0,0 @@ -package com.project.carsharingapp.model; - -public enum RoleName { - MANAGER, - CUSTOMER -} diff --git a/src/main/java/com/project/carsharingapp/repository/RoleRepository.java b/src/main/java/com/project/carsharingapp/repository/RoleRepository.java index a12122f..bb20be2 100644 --- a/src/main/java/com/project/carsharingapp/repository/RoleRepository.java +++ b/src/main/java/com/project/carsharingapp/repository/RoleRepository.java @@ -1,7 +1,9 @@ package com.project.carsharingapp.repository; import com.project.carsharingapp.model.Role; +import java.util.Optional; import org.springframework.data.jpa.repository.JpaRepository; public interface RoleRepository extends JpaRepository { + Optional findByRoleName(Role.RoleName name); } diff --git a/src/main/java/com/project/carsharingapp/repository/SpecificationBuilder.java b/src/main/java/com/project/carsharingapp/repository/SpecificationBuilder.java new file mode 100644 index 0000000..5bc1949 --- /dev/null +++ b/src/main/java/com/project/carsharingapp/repository/SpecificationBuilder.java @@ -0,0 +1,8 @@ +package com.project.carsharingapp.repository; + +import com.project.carsharingapp.dto.rental.RentalSearchParametersDto; +import org.springframework.data.jpa.domain.Specification; + +public interface SpecificationBuilder { + Specification build(RentalSearchParametersDto searchParametersDto); +} diff --git a/src/main/java/com/project/carsharingapp/repository/SpecificationProvider.java b/src/main/java/com/project/carsharingapp/repository/SpecificationProvider.java new file mode 100644 index 0000000..d54a025 --- /dev/null +++ b/src/main/java/com/project/carsharingapp/repository/SpecificationProvider.java @@ -0,0 +1,10 @@ +package com.project.carsharingapp.repository; + +import com.project.carsharingapp.model.Rental; +import org.springframework.data.jpa.domain.Specification; + +public interface SpecificationProvider { + String getKey(); + + Specification getSpecification(String[] params); +} diff --git a/src/main/java/com/project/carsharingapp/repository/SpecificationProviderManager.java b/src/main/java/com/project/carsharingapp/repository/SpecificationProviderManager.java new file mode 100644 index 0000000..eb49691 --- /dev/null +++ b/src/main/java/com/project/carsharingapp/repository/SpecificationProviderManager.java @@ -0,0 +1,5 @@ +package com.project.carsharingapp.repository; + +public interface SpecificationProviderManager { + SpecificationProvider getSpecificationProvider(String key); +} diff --git a/src/main/java/com/project/carsharingapp/repository/UserRepository.java b/src/main/java/com/project/carsharingapp/repository/UserRepository.java index b5e8db2..52ce55d 100644 --- a/src/main/java/com/project/carsharingapp/repository/UserRepository.java +++ b/src/main/java/com/project/carsharingapp/repository/UserRepository.java @@ -3,7 +3,12 @@ import com.project.carsharingapp.model.User; import java.util.Optional; import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.jpa.repository.Query; public interface UserRepository extends JpaRepository { + + @Query("FROM User u LEFT JOIN FETCH u.roles WHERE u.id = :id") + Optional findUserById(Long id); + Optional findByEmail(String email); } diff --git a/src/main/java/com/project/carsharingapp/repository/RentalRepository.java b/src/main/java/com/project/carsharingapp/repository/rentals/RentalRepository.java similarity index 57% rename from src/main/java/com/project/carsharingapp/repository/RentalRepository.java rename to src/main/java/com/project/carsharingapp/repository/rentals/RentalRepository.java index 8ddb1a0..f43194f 100644 --- a/src/main/java/com/project/carsharingapp/repository/RentalRepository.java +++ b/src/main/java/com/project/carsharingapp/repository/rentals/RentalRepository.java @@ -1,12 +1,18 @@ -package com.project.carsharingapp.repository; +package com.project.carsharingapp.repository.rentals; import com.project.carsharingapp.model.Rental; +import java.util.List; import java.util.Optional; import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.data.jpa.repository.Query; +import org.springframework.stereotype.Repository; +@Repository public interface RentalRepository extends JpaRepository { @Query("FROM Rental r LEFT JOIN FETCH r.car " + "LEFT JOIN FETCH r.user WHERE r.id = :id") Optional findById(Long id); + + @Query("FROM Rental r WHERE r.user.id = :userId AND r.isActive = :isActive") + List findRentalsByUserIdAndActiveStatus(Long userId, boolean isActive); } diff --git a/src/main/java/com/project/carsharingapp/repository/rentals/RentalSpecificationBuilder.java b/src/main/java/com/project/carsharingapp/repository/rentals/RentalSpecificationBuilder.java new file mode 100644 index 0000000..dd81770 --- /dev/null +++ b/src/main/java/com/project/carsharingapp/repository/rentals/RentalSpecificationBuilder.java @@ -0,0 +1,36 @@ +package com.project.carsharingapp.repository.rentals; + +import com.project.carsharingapp.dto.rental.RentalSearchParametersDto; +import com.project.carsharingapp.model.Rental; +import com.project.carsharingapp.repository.SpecificationBuilder; +import com.project.carsharingapp.repository.SpecificationProviderManager; +import lombok.RequiredArgsConstructor; +import org.springframework.data.jpa.domain.Specification; +import org.springframework.stereotype.Component; + +@RequiredArgsConstructor +@Component +public class RentalSpecificationBuilder implements SpecificationBuilder { + public static final String USER_ID_KEY = "userId"; + public static final String IS_ACTIVE_KEY = "isActive"; + + private final SpecificationProviderManager rentalSpecificationProviderManager; + + @Override + public Specification build(RentalSearchParametersDto searchParametersDto) { + Specification spec = Specification.where(null); + spec = getRentalSpecification(searchParametersDto.userId(), spec, USER_ID_KEY); + spec = getRentalSpecification(searchParametersDto.isActive(), spec, IS_ACTIVE_KEY); + return spec; + } + + private Specification getRentalSpecification(String[] searchParametersDto, + Specification spec, + String key) { + if (searchParametersDto != null && searchParametersDto.length > 0) { + spec = spec.and(rentalSpecificationProviderManager.getSpecificationProvider(key) + .getSpecification(searchParametersDto)); + } + return spec; + } +} diff --git a/src/main/java/com/project/carsharingapp/repository/rentals/RentalSpecificationProviderManager.java b/src/main/java/com/project/carsharingapp/repository/rentals/RentalSpecificationProviderManager.java new file mode 100644 index 0000000..eb048df --- /dev/null +++ b/src/main/java/com/project/carsharingapp/repository/rentals/RentalSpecificationProviderManager.java @@ -0,0 +1,25 @@ +package com.project.carsharingapp.repository.rentals; + +import com.project.carsharingapp.model.Rental; +import com.project.carsharingapp.repository.SpecificationProvider; +import com.project.carsharingapp.repository.SpecificationProviderManager; +import java.util.List; +import java.util.NoSuchElementException; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Component; + +@RequiredArgsConstructor +@Component +public class RentalSpecificationProviderManager implements SpecificationProviderManager { + private final List> rentalSpecificationProviders; + + @Override + public SpecificationProvider getSpecificationProvider(String key) { + return rentalSpecificationProviders + .stream() + .filter(p -> p.getKey().equals(key)) + .findFirst() + .orElseThrow(() -> new NoSuchElementException( + "Can't find correct specification provider for key: " + key)); + } +} diff --git a/src/main/java/com/project/carsharingapp/repository/rentals/spec/ActiveStatusSpecificationProvider.java b/src/main/java/com/project/carsharingapp/repository/rentals/spec/ActiveStatusSpecificationProvider.java new file mode 100644 index 0000000..a281db9 --- /dev/null +++ b/src/main/java/com/project/carsharingapp/repository/rentals/spec/ActiveStatusSpecificationProvider.java @@ -0,0 +1,22 @@ +package com.project.carsharingapp.repository.rentals.spec; + +import static com.project.carsharingapp.repository.rentals.RentalSpecificationBuilder.IS_ACTIVE_KEY; + +import com.project.carsharingapp.model.Rental; +import com.project.carsharingapp.repository.SpecificationProvider; +import java.util.Arrays; +import org.springframework.data.jpa.domain.Specification; +import org.springframework.stereotype.Component; + +@Component +public class ActiveStatusSpecificationProvider implements SpecificationProvider { + @Override + public String getKey() { + return IS_ACTIVE_KEY; + } + + public Specification getSpecification(String[] params) { + return (root, query, criteriaBuilder) -> root.get(IS_ACTIVE_KEY) + .in(Arrays.stream(params).toArray()); + } +} diff --git a/src/main/java/com/project/carsharingapp/repository/rentals/spec/UserSpecificationProvider.java b/src/main/java/com/project/carsharingapp/repository/rentals/spec/UserSpecificationProvider.java new file mode 100644 index 0000000..ad7f03e --- /dev/null +++ b/src/main/java/com/project/carsharingapp/repository/rentals/spec/UserSpecificationProvider.java @@ -0,0 +1,22 @@ +package com.project.carsharingapp.repository.rentals.spec; + +import static com.project.carsharingapp.repository.rentals.RentalSpecificationBuilder.USER_ID_KEY; + +import com.project.carsharingapp.model.Rental; +import com.project.carsharingapp.repository.SpecificationProvider; +import java.util.Arrays; +import org.springframework.data.jpa.domain.Specification; +import org.springframework.stereotype.Component; + +@Component +public class UserSpecificationProvider implements SpecificationProvider { + @Override + public String getKey() { + return USER_ID_KEY; + } + + public Specification getSpecification(String[] params) { + return (root, query, criteriaBuilder) -> root.get(USER_ID_KEY) + .in(Arrays.stream(params).toArray()); + } +} diff --git a/src/main/java/com/project/carsharingapp/service/NotificationService.java b/src/main/java/com/project/carsharingapp/service/NotificationService.java index 6e1bdd2..30ed54b 100644 --- a/src/main/java/com/project/carsharingapp/service/NotificationService.java +++ b/src/main/java/com/project/carsharingapp/service/NotificationService.java @@ -1,85 +1,5 @@ package com.project.carsharingapp.service; -import com.project.carsharingapp.config.TelegramBotConfig; -import com.project.carsharingapp.model.User; -import com.project.carsharingapp.repository.UserRepository; -import java.util.Optional; -import lombok.RequiredArgsConstructor; -import org.springframework.stereotype.Component; -import org.telegram.telegrambots.bots.TelegramLongPollingBot; -import org.telegram.telegrambots.meta.api.methods.send.SendMessage; -import org.telegram.telegrambots.meta.api.objects.Update; -import org.telegram.telegrambots.meta.exceptions.TelegramApiException; - -@Component -@RequiredArgsConstructor -public class NotificationService extends TelegramLongPollingBot { - private final TelegramBotConfig config; - private final UserRepository userRepository; - - @Override - public void onUpdateReceived(Update update) { - if (update.hasMessage() && update.getMessage().hasText()) { - String messageText = update.getMessage().getText(); - Long chatId = update.getMessage().getChatId(); - switch (messageText) { - case "/start": - startCommandReceived(chatId, update.getMessage().getChat().getFirstName()); - break; - default: - break; - } - if (!messageText.startsWith("/")) { - saveUserChatId(chatId, update.getMessage().getText()); - } - } - } - - private void saveUserChatId(Long chatId, String text) { - Optional optionalUser = userRepository.findByEmail(text); - if (optionalUser.isPresent()) { - User user = optionalUser.get(); - user.setTelegramChatId(chatId); - userRepository.save(user); - sendMessage(chatId, "Success"); - } else { - sendMessage(chatId, "can`t find user"); - } - } - - @Override - public String getBotUsername() { - return config.getBotName(); - } - - @Override - public String getBotToken() { - return config.getToken(); - } - - public void sendNotification(Long userId, String message) { - //need security - Long chatId = 0L; - sendMessage(chatId, message); - } - - private void startCommandReceived(Long chatId, String firstName) { - String answer = "Hi, " + firstName + ", nice to meet you! \n" - + "Enter your username to get started:"; - sendMessage(chatId, answer); - - } - - private void sendMessage(Long chatId, String textMessage) { - SendMessage sendMessage = new SendMessage(); - sendMessage.setChatId(chatId); - sendMessage.setText(textMessage); - - try { - execute(sendMessage); - } catch (TelegramApiException e) { - throw new RuntimeException(e); - } - } - +public interface NotificationService { + void sendMessage(Long userChatId, String message); } diff --git a/src/main/java/com/project/carsharingapp/service/UserService.java b/src/main/java/com/project/carsharingapp/service/UserService.java index 5f1e70e..729ebe5 100644 --- a/src/main/java/com/project/carsharingapp/service/UserService.java +++ b/src/main/java/com/project/carsharingapp/service/UserService.java @@ -1,5 +1,13 @@ -package com.project.carsharingapp.service; - -public interface UserService { - -} +package com.project.carsharingapp.service; + +import com.project.carsharingapp.dto.user.UpdateUserProfileRequestDto; +import com.project.carsharingapp.dto.user.UpdateUserRoleRequestDto; +import com.project.carsharingapp.dto.user.UserDto; + +public interface UserService { + UserDto findById(Long id); + + UserDto updateUserProfile(Long id, UpdateUserProfileRequestDto requestDto); + + UserDto updateUserRole(Long id, UpdateUserRoleRequestDto requestDto); +} diff --git a/src/main/java/com/project/carsharingapp/service/CarServiceImpl.java b/src/main/java/com/project/carsharingapp/service/impl/CarServiceImpl.java similarity index 95% rename from src/main/java/com/project/carsharingapp/service/CarServiceImpl.java rename to src/main/java/com/project/carsharingapp/service/impl/CarServiceImpl.java index 3fe66ab..c3e52bb 100644 --- a/src/main/java/com/project/carsharingapp/service/CarServiceImpl.java +++ b/src/main/java/com/project/carsharingapp/service/impl/CarServiceImpl.java @@ -1,4 +1,4 @@ -package com.project.carsharingapp.service; +package com.project.carsharingapp.service.impl; import com.project.carsharingapp.dto.car.CarDto; import com.project.carsharingapp.dto.car.CreateCarRequestDto; @@ -7,6 +7,7 @@ import com.project.carsharingapp.mapper.CarMapper; import com.project.carsharingapp.model.Car; import com.project.carsharingapp.repository.CarRepository; +import com.project.carsharingapp.service.CarService; import java.util.List; import lombok.RequiredArgsConstructor; import org.springframework.data.domain.Pageable; diff --git a/src/main/java/com/project/carsharingapp/service/impl/NotificationServiceImpl.java b/src/main/java/com/project/carsharingapp/service/impl/NotificationServiceImpl.java new file mode 100644 index 0000000..b6d6f40 --- /dev/null +++ b/src/main/java/com/project/carsharingapp/service/impl/NotificationServiceImpl.java @@ -0,0 +1,17 @@ +package com.project.carsharingapp.service.impl; + +import com.project.carsharingapp.service.NotificationService; +import com.project.carsharingapp.telegram.TelegramBot; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Component; + +@Component +@RequiredArgsConstructor +public class NotificationServiceImpl implements NotificationService { + private final TelegramBot telegramBot; + + @Override + public void sendMessage(Long userChatId, String message) { + telegramBot.sendNotification(userChatId, message); + } +} diff --git a/src/main/java/com/project/carsharingapp/service/impl/PaymentServiceImpl.java b/src/main/java/com/project/carsharingapp/service/impl/PaymentServiceImpl.java index 84ce93c..250e2ca 100644 --- a/src/main/java/com/project/carsharingapp/service/impl/PaymentServiceImpl.java +++ b/src/main/java/com/project/carsharingapp/service/impl/PaymentServiceImpl.java @@ -5,7 +5,7 @@ import com.project.carsharingapp.model.Payment; import com.project.carsharingapp.model.Rental; import com.project.carsharingapp.repository.PaymentRepository; -import com.project.carsharingapp.repository.RentalRepository; +import com.project.carsharingapp.repository.rentals.RentalRepository; import com.project.carsharingapp.service.PaymentService; import com.stripe.exception.StripeException; import com.stripe.model.checkout.Session; diff --git a/src/main/java/com/project/carsharingapp/service/impl/UserServiceImpl.java b/src/main/java/com/project/carsharingapp/service/impl/UserServiceImpl.java new file mode 100644 index 0000000..49c4264 --- /dev/null +++ b/src/main/java/com/project/carsharingapp/service/impl/UserServiceImpl.java @@ -0,0 +1,54 @@ +package com.project.carsharingapp.service.impl; + +import com.project.carsharingapp.dto.user.UpdateUserProfileRequestDto; +import com.project.carsharingapp.dto.user.UpdateUserRoleRequestDto; +import com.project.carsharingapp.dto.user.UserDto; +import com.project.carsharingapp.exception.EntityNotFoundException; +import com.project.carsharingapp.mapper.UserMapper; +import com.project.carsharingapp.model.Role; +import com.project.carsharingapp.model.User; +import com.project.carsharingapp.repository.RoleRepository; +import com.project.carsharingapp.repository.UserRepository; +import com.project.carsharingapp.service.UserService; +import java.util.Optional; +import java.util.Set; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Service; + +@RequiredArgsConstructor +@Service +public class UserServiceImpl implements UserService { + private final UserRepository userRepository; + private final UserMapper userMapper; + private final RoleRepository roleRepository; + + @Override + public UserDto findById(Long id) { + User user = getCurrentUser(id); + return userMapper.toDto(user); + } + + @Override + public UserDto updateUserProfile(Long id, UpdateUserProfileRequestDto requestDto) { + User user = getCurrentUser(id); + user.setEmail(requestDto.getEmail()); + user.setFirstName(requestDto.getFirstName()); + user.setLastName(requestDto.getLastName()); + user.setPassword(requestDto.getPassword()); + return userMapper.toDto(userRepository.save(user)); + } + + @Override + public UserDto updateUserRole(Long id, UpdateUserRoleRequestDto requestDto) { + User user = getCurrentUser(id); + Optional roleName = roleRepository.findByRoleName(requestDto.getRole()); + user.setRoles(Set.of(roleName.orElseThrow( + () -> new RuntimeException("Can't find role " + requestDto.getRole())))); + return userMapper.toDto(userRepository.save(user)); + } + + private User getCurrentUser(Long id) { + return userRepository.findUserById(id).orElseThrow( + () -> new EntityNotFoundException("Can't find user by id " + id)); + } +} diff --git a/src/main/java/com/project/carsharingapp/service/rental/RentalService.java b/src/main/java/com/project/carsharingapp/service/rental/RentalService.java new file mode 100644 index 0000000..6ec1358 --- /dev/null +++ b/src/main/java/com/project/carsharingapp/service/rental/RentalService.java @@ -0,0 +1,15 @@ +package com.project.carsharingapp.service.rental; + +import com.project.carsharingapp.dto.rental.CreateRentalRequestDto; +import com.project.carsharingapp.dto.rental.RentalDto; +import java.util.List; + +public interface RentalService { + RentalDto add(CreateRentalRequestDto requestDto); + + List getByUserIdAndActiveStatus(Long userId, boolean isActive); + + RentalDto getById(Long id); + + RentalDto setActualReturnDay(Long id); +} diff --git a/src/main/java/com/project/carsharingapp/service/rental/RentalServiceImpl.java b/src/main/java/com/project/carsharingapp/service/rental/RentalServiceImpl.java new file mode 100644 index 0000000..fbbb79a --- /dev/null +++ b/src/main/java/com/project/carsharingapp/service/rental/RentalServiceImpl.java @@ -0,0 +1,97 @@ +package com.project.carsharingapp.service.rental; + +import com.project.carsharingapp.dto.rental.CreateRentalRequestDto; +import com.project.carsharingapp.dto.rental.RentalDto; +import com.project.carsharingapp.exception.EntityNotFoundException; +import com.project.carsharingapp.mapper.RentalMapper; +import com.project.carsharingapp.model.Car; +import com.project.carsharingapp.model.Rental; +import com.project.carsharingapp.model.User; +import com.project.carsharingapp.repository.CarRepository; +import com.project.carsharingapp.repository.UserRepository; +import com.project.carsharingapp.repository.rentals.RentalRepository; +import java.time.LocalDateTime; +import java.util.List; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Service; + +@Service +@RequiredArgsConstructor +public class RentalServiceImpl implements RentalService { + private final RentalRepository rentalRepository; + private final RentalMapper rentalMapper; + private final CarRepository carRepository; + private final UserRepository userRepository; + + @Override + public RentalDto add(CreateRentalRequestDto requestDto) { + Rental rental = rentalMapper.toEntity(requestDto); + rental.setCar(getCar(requestDto.getCarId())); + rental.setUser(getUser(requestDto.getCarId())); + rental.setActive(true); + rental = rentalRepository.save(rental); + decreaseCarInventory(requestDto.getCarId()); + return rentalMapper.toDto(rental); + } + + @Override + public List getByUserIdAndActiveStatus(Long userId, boolean isActive) { + List rentals = rentalRepository + .findRentalsByUserIdAndActiveStatus(userId, isActive); + if (rentals == null || rentals.isEmpty()) { + throw new EntityNotFoundException("No rentals found for user id: " + + userId + " and active status: " + isActive); + } + return rentals.stream() + .map(rentalMapper::toDto) + .toList(); + } + + @Override + public RentalDto getById(Long id) { + Rental rental = rentalRepository.findById(id).orElseThrow( + () -> new EntityNotFoundException("Can't find rental by id: " + id) + ); + return rentalMapper.toDto(rental); + } + + @Override + public RentalDto setActualReturnDay(Long id) { + return rentalRepository.findById(id) + .map(rental -> { + rental.setActualReturnDate(LocalDateTime.now()); + rentalRepository.save(rental); + increaseCarInventory(id); + return rentalMapper.toDto(rental); + }) + .orElseThrow(() -> new EntityNotFoundException("Rental " + + "not found by id: " + id)); + } + + private User getUser(Long userId) { + return userRepository.findById(userId) + .orElseThrow(() -> new EntityNotFoundException("User " + + "not found by id: " + userId)); + } + + private Car getCar(Long carId) { + return carRepository.findById(carId) + .orElseThrow(() -> new EntityNotFoundException("Car" + + " not found by id: " + carId)); + } + + private void decreaseCarInventory(Long carId) { + Car car = getCar(carId); + Integer existedInventory = car.getInventory(); + car.setInventory(existedInventory - 1); + carRepository.save(car); + } + + private void increaseCarInventory(Long carId) { + Car car = getCar(carId); + Integer existedInventory = car.getInventory(); + car.setInventory(existedInventory + 1); + carRepository.save(car); + } +} + diff --git a/src/main/java/com/project/carsharingapp/telegram/TelegramBot.java b/src/main/java/com/project/carsharingapp/telegram/TelegramBot.java new file mode 100644 index 0000000..f4361ea --- /dev/null +++ b/src/main/java/com/project/carsharingapp/telegram/TelegramBot.java @@ -0,0 +1,83 @@ +package com.project.carsharingapp.telegram; + +import com.project.carsharingapp.config.TelegramBotConfig; +import com.project.carsharingapp.model.User; +import com.project.carsharingapp.repository.UserRepository; +import java.util.Optional; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Component; +import org.telegram.telegrambots.bots.TelegramLongPollingBot; +import org.telegram.telegrambots.meta.api.methods.send.SendMessage; +import org.telegram.telegrambots.meta.api.objects.Update; +import org.telegram.telegrambots.meta.exceptions.TelegramApiException; + +@Component +@RequiredArgsConstructor +public class TelegramBot extends TelegramLongPollingBot { + private final TelegramBotConfig config; + private final UserRepository userRepository; + + public void onUpdateReceived(Update update) { + if (update.hasMessage() && update.getMessage().hasText()) { + String messageText = update.getMessage().getText(); + Long chatId = update.getMessage().getChatId(); + switch (messageText) { + case "/start": + startCommandReceived(chatId, update.getMessage().getChat().getFirstName()); + break; + default: + break; + } + if (!messageText.startsWith("/")) { + saveUserChatId(chatId, update.getMessage().getText()); + } + } + } + + private void saveUserChatId(Long chatId, String text) { + Optional optionalUser = userRepository.findByEmail(text); + if (optionalUser.isPresent()) { + User user = optionalUser.get(); + user.setTelegramChatId(chatId); + userRepository.save(user); + sendMessage(chatId, "Success"); + } else { + sendMessage(chatId, "can`t find user"); + } + } + + @Override + public String getBotUsername() { + return config.getBotName(); + } + + @Override + public String getBotToken() { + return config.getToken(); + } + + public void sendNotification(Long userId, String message) { + //need security + Long chatId = 0L; + sendMessage(chatId, message); + } + + private void startCommandReceived(Long chatId, String firstName) { + String answer = "Hi, " + firstName + ", nice to meet you! \n" + + "Enter your username to get started:"; + sendMessage(chatId, answer); + + } + + private void sendMessage(Long chatId, String textMessage) { + SendMessage sendMessage = new SendMessage(); + sendMessage.setChatId(chatId); + sendMessage.setText(textMessage); + + try { + execute(sendMessage); + } catch (TelegramApiException e) { + throw new RuntimeException(e); + } + } +} diff --git a/src/main/java/com/project/carsharingapp/config/TelegramBotInitializer.java b/src/main/java/com/project/carsharingapp/telegram/TelegramBotInitializer.java similarity index 75% rename from src/main/java/com/project/carsharingapp/config/TelegramBotInitializer.java rename to src/main/java/com/project/carsharingapp/telegram/TelegramBotInitializer.java index 951c334..a37b81a 100644 --- a/src/main/java/com/project/carsharingapp/config/TelegramBotInitializer.java +++ b/src/main/java/com/project/carsharingapp/telegram/TelegramBotInitializer.java @@ -1,6 +1,5 @@ -package com.project.carsharingapp.config; +package com.project.carsharingapp.telegram; -import com.project.carsharingapp.service.NotificationService; import lombok.RequiredArgsConstructor; import org.springframework.context.event.ContextRefreshedEvent; import org.springframework.context.event.EventListener; @@ -12,12 +11,12 @@ @Component @RequiredArgsConstructor public class TelegramBotInitializer { - private final NotificationService notificationService; + private final TelegramBot telegramBot; @EventListener({ContextRefreshedEvent.class}) public void init() throws TelegramApiException { TelegramBotsApi telegramBotsApi = new TelegramBotsApi(DefaultBotSession.class); - telegramBotsApi.registerBot(notificationService); + telegramBotsApi.registerBot(telegramBot); } } diff --git a/src/main/resources/application.properties b/src/main/resources/application.properties index 415e579..06beab8 100644 --- a/src/main/resources/application.properties +++ b/src/main/resources/application.properties @@ -1,6 +1,6 @@ spring.datasource.url=jdbc:mysql://localhost:3306/car_sharing_db?serverTimezone=UTC spring.datasource.username=root -spring.datasource.password=redblack0root +spring.datasource.password=root123 spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver server.servlet.context-path=/api diff --git a/src/main/resources/db/changelog/changes/add-default-cars-to-cars-table.yaml b/src/main/resources/db/changelog/changes/add-default-cars-to-cars-table.yaml deleted file mode 100644 index d1927d2..0000000 --- a/src/main/resources/db/changelog/changes/add-default-cars-to-cars-table.yaml +++ /dev/null @@ -1,115 +0,0 @@ -databaseChangeLog: - - changeSet: - id: insert-car-1 - author: Dmytro Varukha - changes: - - insert: - tableName: cars - columns: - - column: - name: model - value: "Toyota Corolla" - - column: - name: brand - value: "Toyota" - - column: - name: inventory - value: 10 - - column: - name: car_type - value: "SEDAN" - - column: - name: daily_fee - value: 50.00 - - - changeSet: - id: insert-car-2 - author: Dmytro Varukha - changes: - - insert: - tableName: cars - columns: - - column: - name: model - value: "Honda Civic" - - column: - name: brand - value: "Honda" - - column: - name: inventory - value: 8 - - column: - name: car_type - value: "SEDAN" - - column: - name: daily_fee - value: 45.00 - - - changeSet: - id: insert-car-3 - author: Dmytro Varukha - changes: - - insert: - tableName: cars - columns: - - column: - name: model - value: "Ford Mustang" - - column: - name: brand - value: "Ford" - - column: - name: inventory - value: 5 - - column: - name: car_type - value: "UNIVERSAL" - - column: - name: daily_fee - value: 80.00 - - - changeSet: - id: insert-car-4 - author: Dmytro Varukha - changes: - - insert: - tableName: cars - columns: - - column: - name: model - value: "Volkswagen Golf" - - column: - name: brand - value: "Volkswagen" - - column: - name: inventory - value: 7 - - column: - name: car_type - value: "HATCHBACK" - - column: - name: daily_fee - value: 55.00 - - - changeSet: - id: insert-car-5 - author: Dmytro Varukha - changes: - - insert: - tableName: cars - columns: - - column: - name: model - value: "Chevrolet Tahoe" - - column: - name: brand - value: "Chevrolet" - - column: - name: inventory - value: 4 - - column: - name: car_type - value: "SUV" - - column: - name: daily_fee - value: 70.00 diff --git a/src/main/resources/db/changelog/changes/add-default-rentals-to-rentals-table.yaml b/src/main/resources/db/changelog/changes/add-default-rentals-to-rentals-table.yaml deleted file mode 100644 index 492eec0..0000000 --- a/src/main/resources/db/changelog/changes/add-default-rentals-to-rentals-table.yaml +++ /dev/null @@ -1,60 +0,0 @@ -databaseChangeLog: - - changeSet: - id: insert-rental-1 - author: Dmytro Varukha - changes: - - insert: - tableName: rentals - columns: - - column: - name: rental_date - value: "2023-10-15 09:00:00" - - column: - name: return_date - value: "2023-10-18 15:00:00" - - column: - name: car_id - value: 1 - - column: - name: user_id - value: 1 - - - changeSet: - id: insert-rental-2 - author: Dmytro Varukha - changes: - - insert: - tableName: rentals - columns: - - column: - name: rental_date - value: "2023-10-20 11:30:00" - - column: - name: return_date - value: "2023-10-25 10:15:00" - - column: - name: car_id - value: 2 - - column: - name: user_id - value: 2 - - - changeSet: - id: insert-rental-3 - author: Dmytro Varukha - changes: - - insert: - tableName: rentals - columns: - - column: - name: rental_date - value: "2023-10-12 14:45:00" - - column: - name: return_date - value: "2023-10-16 16:30:00" - - column: - name: car_id - value: 3 - - column: - name: user_id - value: 3 diff --git a/src/main/resources/db/changelog/changes/add-default-users-to-users-table.yaml b/src/main/resources/db/changelog/changes/add-default-users-to-users-table.yaml deleted file mode 100644 index 75ceb75..0000000 --- a/src/main/resources/db/changelog/changes/add-default-users-to-users-table.yaml +++ /dev/null @@ -1,69 +0,0 @@ -databaseChangeLog: - - changeSet: - id: insert-user-1 - author: Dmytro Varukha - changes: - - insert: - tableName: users - columns: - - column: - name: email - value: user1@example.com - - column: - name: first_name - value: John - - column: - name: last_name - value: Doe - - column: - name: password - value: password1 - - column: - name: telegram_chat_id - value: 1234567 - - - changeSet: - id: insert-user-2 - author: Dmytro Varukha - changes: - - insert: - tableName: users - columns: - - column: - name: email - value: user2@example.com - - column: - name: first_name - value: Jane - - column: - name: last_name - value: Smith - - column: - name: password - value: password2 - - column: - name: telegram_chat_id - value: 9876543 - - - changeSet: - id: insert-user-3 - author: Dmytro Varukha - changes: - - insert: - tableName: users - columns: - - column: - name: email - value: user3@example.com - - column: - name: first_name - value: Alice - - column: - name: last_name - value: Johnson - - column: - name: password - value: password3 - - column: - name: telegram_chat_id - value: 5555555 diff --git a/src/main/resources/db/changelog/changes/add-is-active-column-to-rental-table.yaml b/src/main/resources/db/changelog/changes/add-is-active-column-to-rental-table.yaml new file mode 100644 index 0000000..b69f51a --- /dev/null +++ b/src/main/resources/db/changelog/changes/add-is-active-column-to-rental-table.yaml @@ -0,0 +1,13 @@ +databaseChangeLog: + - changeSet: + id: add-is-active-column-to-rental-table + author: Dmytro Varukha + changes: + - addColumn: + tableName: rentals + columns: + - column: + name: is_active + type: boolean + 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 e538309..ef49be4 100644 --- a/src/main/resources/db/changelog/db.changelog-master.yaml +++ b/src/main/resources/db/changelog/db.changelog-master.yaml @@ -12,8 +12,4 @@ databaseChangeLog: - include: file: db/changelog/changes/create-payments-table.yaml - include: - file: db/changelog/changes/add-default-cars-to-cars-table.yaml - - include: - file: db/changelog/changes/add-default-users-to-users-table.yaml - - include: - file: db/changelog/changes/add-default-rentals-to-rentals-table.yaml + file: db/changelog/changes/add-is-active-column-to-rental-table.yaml