Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add bookings #4

Merged
merged 5 commits into from
Aug 5, 2024
Merged
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
feat: add booking and comment.
Nikolaev-Java committed Aug 2, 2024

Verified

This commit was created on GitHub.com and signed with GitHub’s verified signature. The key has expired.
commit 64380a7848ed5187174f3b959cb788483dd04d28
34 changes: 30 additions & 4 deletions pom.xml
Original file line number Diff line number Diff line change
@@ -28,10 +28,6 @@
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>org.postgresql</groupId>
<artifactId>postgresql</artifactId>
</dependency>
<dependency>
<groupId>org.postgresql</groupId>
<artifactId>postgresql</artifactId>
@@ -61,6 +57,12 @@
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-validation</artifactId>
</dependency>
<dependency>
<groupId>com.querydsl</groupId>
<artifactId>querydsl-jpa</artifactId>
<classifier>jakarta</classifier>
<version>5.1.0</version>
</dependency>
</dependencies>

<build>
@@ -71,6 +73,30 @@
</resource>
</resources>
<plugins>
<plugin>
<groupId>com.mysema.maven</groupId>
<artifactId>apt-maven-plugin</artifactId>
<version>1.1.3</version>
<executions>
<execution>
<goals>
<goal>process</goal>
</goals>
<configuration>
<outputDirectory>target/generated-sources/java</outputDirectory>
<processor>com.querydsl.apt.jpa.JPAAnnotationProcessor</processor>
</configuration>
</execution>
</executions>
<dependencies>
<dependency>
<groupId>com.querydsl</groupId>
<artifactId>querydsl-apt</artifactId>
<classifier>jakarta</classifier>
<version>5.1.0</version>
</dependency>
</dependencies>
</plugin>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
7 changes: 0 additions & 7 deletions src/main/java/ru/practicum/shareit/booking/Booking.java

This file was deleted.

59 changes: 59 additions & 0 deletions src/main/java/ru/practicum/shareit/booking/BookingController.java
Original file line number Diff line number Diff line change
@@ -1,12 +1,71 @@
package ru.practicum.shareit.booking;

import jakarta.validation.Valid;
import lombok.RequiredArgsConstructor;
import org.springframework.validation.annotation.Validated;
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.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestHeader;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import ru.practicum.shareit.booking.dto.BookingDtoResponse;
import ru.practicum.shareit.booking.dto.NewBookingDtoRequest;
import ru.practicum.shareit.booking.model.BookingState;
import ru.practicum.shareit.booking.service.BookingService;
import ru.practicum.shareit.validationMarker.Marker;

import java.util.List;

/**
* TODO Sprint add-bookings.
*/
@RestController
@RequiredArgsConstructor
@RequestMapping(path = "/bookings")
@Validated
public class BookingController {
private final BookingService bookingService;
private static final String USER_ID = "X-Sharer-User-Id";

@PostMapping
@Validated(Marker.OnCreate.class)
public BookingDtoResponse createBooking(@RequestBody @Valid NewBookingDtoRequest dto,
@RequestHeader(USER_ID) long userId) {
dto.setBookerId(userId);
return bookingService.createBooking(dto);
}

@PatchMapping("/{bookingId}")
public BookingDtoResponse decideRent(@PathVariable long bookingId,
@RequestHeader(USER_ID) long userId,
@RequestParam boolean approved) {
if (approved) {
return bookingService.approveBooking(bookingId, userId);
} else
return bookingService.rejectBooking(bookingId, userId);
}

@GetMapping("/{bookingId}")
public BookingDtoResponse getBookingByIdOfBookerOrOwner(@PathVariable long bookingId,
@RequestHeader(USER_ID) long userId) {
return bookingService.getBookingByIdOfBookerOrOwner(bookingId, userId);
}

@GetMapping
public List<BookingDtoResponse> getAllBookingsByBooker(@RequestHeader(USER_ID) long userId,
@RequestParam(name = "state", defaultValue = "ALL") String stateParam) {
BookingState state = BookingState.parseString(stateParam);
return bookingService.getAllBookingsByBooker(userId, state);
}

@GetMapping("/owner")
public List<BookingDtoResponse> getAllBookingsByOwner(@RequestHeader(USER_ID) long userId,
@RequestParam(name = "state", defaultValue = "ALL") String stateParam) {
BookingState state = BookingState.parseString(stateParam);
return bookingService.getAllBookingsByOwner(userId, state);
}
}
54 changes: 54 additions & 0 deletions src/main/java/ru/practicum/shareit/booking/BookingRepository.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
package ru.practicum.shareit.booking;

import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.querydsl.QuerydslPredicateExecutor;
import ru.practicum.shareit.booking.model.Booking;

import java.time.LocalDateTime;
import java.util.List;
import java.util.Optional;

public interface BookingRepository extends JpaRepository<Booking, Long>, QuerydslPredicateExecutor<Booking> {
@Query(value = """
select b from Booking as b where b.id=?1 and (b.booker.id=?2 or b.item.owner.id=?2)
""")
Optional<Booking> findById(long bookingId, long userId);

@Query(value = """
select b from Booking as b where b.item.id in ?1
and b.status='APPROVED'
and b.startTime < ?2
order by b.startTime desc
limit 1
""")
Optional<Booking> findByItemLastBooking(Long itemId, LocalDateTime now);

@Query(value = """
select b from Booking as b where b.item.id in ?1
and b.status='APPROVED'
and b.startTime > ?2
order by b.startTime asc
limit 1
""")
Optional<Booking> findByItemNextBooking(Long itemId, LocalDateTime now);

@Query(value = """
select b from Booking as b where b.item.id in ?1
and b.status='APPROVED'
and b.startTime < ?2
order by b.startTime desc
""")
List<Booking> findAllByItemsLastBooking(List<Long> itemIds, LocalDateTime now);

@Query(value = """
select b from Booking as b where b.item.id in ?1
and b.status='APPROVED'
and b.startTime > ?2
order by b.startTime asc
""")
List<Booking> findAllByItemsNextBooking(List<Long> itemIds, LocalDateTime now);

List<Booking> findAllByItemId(Long itemId);
}

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
package ru.practicum.shareit.booking.dto;

import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Getter;
import lombok.Setter;

import java.time.LocalDateTime;

/**
* TODO Sprint add-bookings.
*/
@Getter
@Setter
@Builder
public class BookingDtoResponse {
private long id;
private ItemDtoResponse item;
private UserDtoResponse booker;
private LocalDateTime start;
private LocalDateTime end;
private String status;

@Setter
@Getter
@Builder
static class ItemDtoResponse {

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

может тут использовать поле с типом ItemDto просто?

Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

дело в том что в тестах постман проверяются поля item.id и item.name. То есть в json должен объект с такими полями.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ну так оно и будет, если сделать ItemDto item

Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ну в ItemDto есть еще поля description и available, а они в выводе не нужны, поэтому и внутренний класс в котором полей меньше. Я просто сделал дополнительные ДТО с меньшим количеством полей, только те которые проверяются в постман тестах

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

не нужны, но ты не должен создавать дто под каждую необходимость, используй те поля, которые нужны. Так как ты делаешь сейчас ты перегружаешь свою сущность. В общем так не делают)

private long id;
private String name;
}

@Setter
@Getter
@AllArgsConstructor
static class UserDtoResponse {

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

а вместо этого long userId

Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

здесь так же по тестам проверяется booker.id.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

а тут тогда BookerDto booker
Проверь, должно работать

private long id;
}
}
41 changes: 41 additions & 0 deletions src/main/java/ru/practicum/shareit/booking/dto/BookingMapper.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
package ru.practicum.shareit.booking.dto;

import org.springframework.stereotype.Component;
import ru.practicum.shareit.booking.model.Booking;
import ru.practicum.shareit.booking.model.Status;
import ru.practicum.shareit.item.Item;
import ru.practicum.shareit.user.User;

import java.util.List;

@Component
public class BookingMapper {

public Booking toNewBooking(NewBookingDtoRequest dto, Item item, User booker) {

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

хорошо бы проверять на null входящие в метод параметры

Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

если проверю дто на нулл, этого же достаточно будет? item и booker если будет нулл то NPE не должно быть, методы их не вызываем.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

да достаточно проверить объект

return Booking.builder()
.item(item)
.booker(booker)
.startTime(dto.getStart())
.endTime(dto.getEnd())
.status(Status.WAITING)
.build();
}

public BookingDtoResponse toBookingDtoResponse(Booking booking) {
return BookingDtoResponse.builder()
.id(booking.getId())
.item(BookingDtoResponse.ItemDtoResponse.builder()
.id(booking.getItem().getId())
.name(booking.getItem().getName())
.build())
.booker(new BookingDtoResponse.UserDtoResponse(booking.getBooker().getId()))
.start(booking.getStartTime())
.end(booking.getEndTime())
.status(booking.getStatus().toString())
.build();
}

public List<BookingDtoResponse> toBookingDtoResponse(List<Booking> bookings) {
return bookings.stream().map(this::toBookingDtoResponse).toList();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
package ru.practicum.shareit.booking.dto;

import jakarta.validation.constraints.Future;
import jakarta.validation.constraints.FutureOrPresent;
import jakarta.validation.constraints.NotNull;
import lombok.Builder;
import lombok.Getter;
import lombok.Setter;
import ru.practicum.shareit.booking.validation.StartBeforeEnd;
import ru.practicum.shareit.validationMarker.Marker;

import java.time.LocalDateTime;

@Getter
@Setter
@Builder
@StartBeforeEnd
public class NewBookingDtoRequest {
@NotNull(message = "Item id must not be null")
private Long itemId;
private long bookerId;
@NotNull(message = "Start time booking must not be null", groups = Marker.OnCreate.class)
@FutureOrPresent(message = "Start time cannot be in the past", groups = Marker.OnCreate.class)
private LocalDateTime start;
@NotNull(message = "End time booking must not be null", groups = Marker.OnCreate.class)
@Future(message = "End time cannot be in the past", groups = Marker.OnCreate.class)
private LocalDateTime end;
}
52 changes: 52 additions & 0 deletions src/main/java/ru/practicum/shareit/booking/model/Booking.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
package ru.practicum.shareit.booking.model;

import jakarta.persistence.Column;
import jakarta.persistence.Entity;
import jakarta.persistence.EnumType;
import jakarta.persistence.Enumerated;
import jakarta.persistence.FetchType;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.GenerationType;
import jakarta.persistence.Id;
import jakarta.persistence.JoinColumn;
import jakarta.persistence.ManyToOne;
import jakarta.persistence.Table;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.EqualsAndHashCode;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
import ru.practicum.shareit.item.Item;
import ru.practicum.shareit.user.User;

import java.time.LocalDateTime;

/**
* TODO Sprint add-bookings.
*/
@Getter
@Setter
@Builder
@NoArgsConstructor
@AllArgsConstructor
@EqualsAndHashCode(of = "id")
@Entity
@Table(name = "bookings")
public class Booking {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "item_id")
private Item item;
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "user_id")
private User booker;
@Column(name = "start_time")
private LocalDateTime startTime;
@Column(name = "end_time")
private LocalDateTime endTime;
@Enumerated(EnumType.STRING)
private Status status;
}
17 changes: 17 additions & 0 deletions src/main/java/ru/practicum/shareit/booking/model/BookingState.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
package ru.practicum.shareit.booking.model;

public enum BookingState {
ALL, CURRENT, PAST, FUTURE, WAITING, REJECTED;

public static BookingState parseString(String state) {
return switch (state.toUpperCase()) {
case "ALL" -> ALL;
case "CURRENT" -> CURRENT;
case "PAST" -> PAST;
case "FUTURE" -> FUTURE;
case "WAITING" -> WAITING;
case "REJECTED" -> REJECTED;
default -> throw new IllegalArgumentException("Unknown state: " + state);
};
}
}
5 changes: 5 additions & 0 deletions src/main/java/ru/practicum/shareit/booking/model/Status.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
package ru.practicum.shareit.booking.model;

public enum Status {
WAITING, APPROVED, REJECTED, CANCELLED
}
Loading