From 6c632daf1ee3c436d2c8d18ed91e997df346f955 Mon Sep 17 00:00:00 2001 From: omniick Date: Thu, 11 Dec 2025 14:02:30 +0400 Subject: [PATCH] all fix --- src/main/java/com/example/nto/App.java | 5 + .../nto/controller/BookingController.java | 38 ++++++ .../nto/controller/EmployeeController.java | 20 ++++ .../advice/GlobalExceptionHandler.java | 29 +++++ .../java/com/example/nto/dto/BookingInfo.java | 13 +++ .../example/nto/dto/CreateBookingRequest.java | 16 +++ .../java/com/example/nto/dto/PlaceInfo.java | 13 +++ .../com/example/nto/dto/UserInfoResponse.java | 17 +++ .../java/com/example/nto/entity/Booking.java | 15 ++- .../java/com/example/nto/entity/Employee.java | 7 ++ .../java/com/example/nto/entity/Place.java | 7 +- .../nto/repository/BookingRepository.java | 14 ++- .../nto/repository/EmployeeRepository.java | 11 +- .../nto/repository/PlaceRepository.java | 7 +- .../example/nto/service/BookingService.java | 14 +++ .../example/nto/service/EmployeeService.java | 5 + .../nto/service/impl/BookingServiceImpl.java | 108 +++++++++++++++++- .../nto/service/impl/EmployeeServiceImpl.java | 15 +++ 18 files changed, 337 insertions(+), 17 deletions(-) create mode 100644 src/main/java/com/example/nto/controller/advice/GlobalExceptionHandler.java create mode 100644 src/main/java/com/example/nto/dto/BookingInfo.java create mode 100644 src/main/java/com/example/nto/dto/CreateBookingRequest.java create mode 100644 src/main/java/com/example/nto/dto/PlaceInfo.java create mode 100644 src/main/java/com/example/nto/dto/UserInfoResponse.java diff --git a/src/main/java/com/example/nto/App.java b/src/main/java/com/example/nto/App.java index e453f89..e5435c1 100644 --- a/src/main/java/com/example/nto/App.java +++ b/src/main/java/com/example/nto/App.java @@ -1,12 +1,17 @@ package com.example.nto; +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; + /** * TODO: ДОРАБОТАТЬ в рамках задания * ================================= * МОЖНО: Добавлять методы, аннотации, зависимости * НЕЛЬЗЯ: Изменять название класса и пакета */ +@SpringBootApplication public class App { public static void main(String[] args) { + SpringApplication.run(App.class, args); } } diff --git a/src/main/java/com/example/nto/controller/BookingController.java b/src/main/java/com/example/nto/controller/BookingController.java index 9885f84..8839c4f 100644 --- a/src/main/java/com/example/nto/controller/BookingController.java +++ b/src/main/java/com/example/nto/controller/BookingController.java @@ -1,10 +1,48 @@ package com.example.nto.controller; +import com.example.nto.dto.CreateBookingRequest; +import com.example.nto.dto.PlaceInfo; +import com.example.nto.dto.UserInfoResponse; +import com.example.nto.service.BookingService; +import com.example.nto.service.EmployeeService; +import lombok.RequiredArgsConstructor; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.*; + +import java.time.LocalDate; +import java.util.List; +import java.util.Map; + /** * TODO: ДОРАБОТАТЬ в рамках задания * ================================= * МОЖНО: Добавлять методы, аннотации, зависимости * НЕЛЬЗЯ: Изменять название класса и пакета */ +@RestController +@RequestMapping("/api/{code}") +@RequiredArgsConstructor public class BookingController { + + private final BookingService bookingService; + private final EmployeeService employeeService; + + @GetMapping("/info") + public ResponseEntity getUserInfo(@PathVariable String code) { + return ResponseEntity.ok(bookingService.getUserInfo(code)); + } + + @GetMapping("/booking") + public ResponseEntity>> getAvailablePlaces(@PathVariable String code) { + employeeService.findByCode(code) + .orElseThrow(() -> new jakarta.persistence.EntityNotFoundException("Employee not found with code: " + code)); + return ResponseEntity.ok(bookingService.getAvailablePlaces()); + } + + @PostMapping("/book") + public ResponseEntity createBooking(@PathVariable String code, @RequestBody CreateBookingRequest request) { + bookingService.createBooking(code, request); + return ResponseEntity.status(HttpStatus.CREATED).build(); + } } diff --git a/src/main/java/com/example/nto/controller/EmployeeController.java b/src/main/java/com/example/nto/controller/EmployeeController.java index 47658f9..3e1e535 100644 --- a/src/main/java/com/example/nto/controller/EmployeeController.java +++ b/src/main/java/com/example/nto/controller/EmployeeController.java @@ -1,10 +1,30 @@ package com.example.nto.controller; +import com.example.nto.service.EmployeeService; +import lombok.RequiredArgsConstructor; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + /** * TODO: ДОРАБОТАТЬ в рамках задания * ================================= * МОЖНО: Добавлять методы, аннотации, зависимости * НЕЛЬЗЯ: Изменять название класса и пакета */ +@RestController +@RequestMapping("/api/{code}") +@RequiredArgsConstructor public class EmployeeController { + + private final EmployeeService employeeService; + + @GetMapping("/auth") + public ResponseEntity auth(@PathVariable String code) { + employeeService.findByCode(code) + .orElseThrow(() -> new jakarta.persistence.EntityNotFoundException("Employee not found with code: " + code)); + return ResponseEntity.ok().build(); + } } diff --git a/src/main/java/com/example/nto/controller/advice/GlobalExceptionHandler.java b/src/main/java/com/example/nto/controller/advice/GlobalExceptionHandler.java new file mode 100644 index 0000000..a694fba --- /dev/null +++ b/src/main/java/com/example/nto/controller/advice/GlobalExceptionHandler.java @@ -0,0 +1,29 @@ +package com.example.nto.controller.advice; + +import jakarta.persistence.EntityNotFoundException; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.ControllerAdvice; +import org.springframework.web.bind.annotation.ExceptionHandler; + +@ControllerAdvice +public class GlobalExceptionHandler { + + @ExceptionHandler(EntityNotFoundException.class) + public ResponseEntity handleEntityNotFoundException(EntityNotFoundException ex) { + if (ex.getMessage().contains("Employee not found")) { + return new ResponseEntity<>(ex.getMessage(), HttpStatus.UNAUTHORIZED); + } + return new ResponseEntity<>(ex.getMessage(), HttpStatus.BAD_REQUEST); + } + + @ExceptionHandler(IllegalStateException.class) + public ResponseEntity handleIllegalStateException(IllegalStateException ex) { + return new ResponseEntity<>(ex.getMessage(), HttpStatus.CONFLICT); + } + + @ExceptionHandler(IllegalArgumentException.class) + public ResponseEntity handleIllegalArgumentException(IllegalArgumentException ex) { + return new ResponseEntity<>(ex.getMessage(), HttpStatus.BAD_REQUEST); + } +} diff --git a/src/main/java/com/example/nto/dto/BookingInfo.java b/src/main/java/com/example/nto/dto/BookingInfo.java new file mode 100644 index 0000000..fa21fdb --- /dev/null +++ b/src/main/java/com/example/nto/dto/BookingInfo.java @@ -0,0 +1,13 @@ +package com.example.nto.dto; + +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +@Data +@AllArgsConstructor +@NoArgsConstructor +public class BookingInfo { + private long id; + private String place; +} diff --git a/src/main/java/com/example/nto/dto/CreateBookingRequest.java b/src/main/java/com/example/nto/dto/CreateBookingRequest.java new file mode 100644 index 0000000..3149ddd --- /dev/null +++ b/src/main/java/com/example/nto/dto/CreateBookingRequest.java @@ -0,0 +1,16 @@ +package com.example.nto.dto; + + +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.time.LocalDate; + +@Data +@AllArgsConstructor +@NoArgsConstructor +public class CreateBookingRequest { + private LocalDate date; + private Long placeId; +} diff --git a/src/main/java/com/example/nto/dto/PlaceInfo.java b/src/main/java/com/example/nto/dto/PlaceInfo.java new file mode 100644 index 0000000..af60d8d --- /dev/null +++ b/src/main/java/com/example/nto/dto/PlaceInfo.java @@ -0,0 +1,13 @@ +package com.example.nto.dto; + +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +@Data +@AllArgsConstructor +@NoArgsConstructor +public class PlaceInfo { + private long id; + private String place; +} diff --git a/src/main/java/com/example/nto/dto/UserInfoResponse.java b/src/main/java/com/example/nto/dto/UserInfoResponse.java new file mode 100644 index 0000000..7392ce8 --- /dev/null +++ b/src/main/java/com/example/nto/dto/UserInfoResponse.java @@ -0,0 +1,17 @@ +package com.example.nto.dto; + +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.time.LocalDate; +import java.util.Map; + +@Data +@AllArgsConstructor +@NoArgsConstructor +public class UserInfoResponse { + private String name; + private String photoUrl; + private Map booking; +} diff --git a/src/main/java/com/example/nto/entity/Booking.java b/src/main/java/com/example/nto/entity/Booking.java index 21c1981..1372ac0 100644 --- a/src/main/java/com/example/nto/entity/Booking.java +++ b/src/main/java/com/example/nto/entity/Booking.java @@ -1,8 +1,6 @@ package com.example.nto.entity; -import jakarta.persistence.FetchType; -import jakarta.persistence.JoinColumn; -import jakarta.persistence.ManyToOne; +import jakarta.persistence.*; import lombok.AllArgsConstructor; import lombok.Builder; import lombok.Data; @@ -21,15 +19,22 @@ import java.time.LocalDate; @Builder @NoArgsConstructor @AllArgsConstructor +@Entity +@Table(name = "booking") public class Booking { + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) private long id; + @Column(nullable = false) private LocalDate date; - @ManyToOne(targetEntity = Place.class, fetch = FetchType.LAZY) - @JoinColumn(name = "place_id") + @ManyToOne(fetch = FetchType.LAZY) + @JoinColumn(name = "place_id", nullable = false) private Place place; + @ManyToOne(fetch = FetchType.LAZY) + @JoinColumn(name = "employee_id", nullable = false) private Employee employee; } diff --git a/src/main/java/com/example/nto/entity/Employee.java b/src/main/java/com/example/nto/entity/Employee.java index a52102b..1cc68a4 100644 --- a/src/main/java/com/example/nto/entity/Employee.java +++ b/src/main/java/com/example/nto/entity/Employee.java @@ -19,14 +19,21 @@ import java.util.List; @Builder @NoArgsConstructor @AllArgsConstructor +@Entity +@Table(name = "employee") public class Employee { + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) private long id; + @Column(nullable = false) private String name; + @Column(nullable = false, unique = true) private String code; + @Column(name = "photo_url") private String photoUrl; @OneToMany(mappedBy = "employee", cascade = CascadeType.ALL, fetch = FetchType.LAZY) diff --git a/src/main/java/com/example/nto/entity/Place.java b/src/main/java/com/example/nto/entity/Place.java index 00c253b..6c809e8 100644 --- a/src/main/java/com/example/nto/entity/Place.java +++ b/src/main/java/com/example/nto/entity/Place.java @@ -1,8 +1,6 @@ package com.example.nto.entity; -import jakarta.persistence.GeneratedValue; -import jakarta.persistence.GenerationType; -import jakarta.persistence.Id; +import jakarta.persistence.*; import lombok.AllArgsConstructor; import lombok.Builder; import lombok.Data; @@ -19,11 +17,14 @@ import lombok.NoArgsConstructor; @Builder @NoArgsConstructor @AllArgsConstructor +@Entity +@Table(name = "place") public class Place { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private long id; + @Column(name = "place_name", nullable = false, unique = true) private String place; } diff --git a/src/main/java/com/example/nto/repository/BookingRepository.java b/src/main/java/com/example/nto/repository/BookingRepository.java index 303bb54..faa74fb 100644 --- a/src/main/java/com/example/nto/repository/BookingRepository.java +++ b/src/main/java/com/example/nto/repository/BookingRepository.java @@ -1,10 +1,22 @@ package com.example.nto.repository; +import com.example.nto.entity.Booking; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.stereotype.Repository; + +import java.time.LocalDate; +import java.util.List; + /** * TODO: ДОРАБОТАТЬ в рамках задания * ================================= * МОЖНО: Добавлять методы, аннотации, зависимости * НЕЛЬЗЯ: Изменять название класса и пакета */ -public interface BookingRepository { +@Repository +public interface BookingRepository extends JpaRepository { + List findAllByDateBetween(LocalDate startDate, LocalDate endDate); + boolean existsByDateAndPlaceId(LocalDate date, Long placeId); + + List findAllByEmployeeId(Long employeeId); } diff --git a/src/main/java/com/example/nto/repository/EmployeeRepository.java b/src/main/java/com/example/nto/repository/EmployeeRepository.java index 210d29c..f05aa8d 100644 --- a/src/main/java/com/example/nto/repository/EmployeeRepository.java +++ b/src/main/java/com/example/nto/repository/EmployeeRepository.java @@ -1,10 +1,19 @@ package com.example.nto.repository; +import com.example.nto.entity.Employee; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.stereotype.Repository; + +import java.util.Optional; + /** * TODO: ДОРАБОТАТЬ в рамках задания * ================================= * МОЖНО: Добавлять методы, аннотации, зависимости * НЕЛЬЗЯ: Изменять название класса и пакета */ -public interface EmployeeRepository { +@Repository +public interface EmployeeRepository extends JpaRepository { + + Optional findByCode(String code); } diff --git a/src/main/java/com/example/nto/repository/PlaceRepository.java b/src/main/java/com/example/nto/repository/PlaceRepository.java index d3bea1d..425dacb 100644 --- a/src/main/java/com/example/nto/repository/PlaceRepository.java +++ b/src/main/java/com/example/nto/repository/PlaceRepository.java @@ -1,10 +1,15 @@ package com.example.nto.repository; +import com.example.nto.entity.Place; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.stereotype.Repository; + /** * TODO: ДОРАБОТАТЬ в рамках задания * ================================= * МОЖНО: Добавлять методы, аннотации, зависимости * НЕЛЬЗЯ: Изменять название класса и пакета */ -public interface PlaceRepository { +@Repository +public interface PlaceRepository extends JpaRepository { } diff --git a/src/main/java/com/example/nto/service/BookingService.java b/src/main/java/com/example/nto/service/BookingService.java index 31ec148..1f270dd 100644 --- a/src/main/java/com/example/nto/service/BookingService.java +++ b/src/main/java/com/example/nto/service/BookingService.java @@ -1,5 +1,13 @@ package com.example.nto.service; +import com.example.nto.dto.CreateBookingRequest; +import com.example.nto.dto.PlaceInfo; +import com.example.nto.dto.UserInfoResponse; + +import java.time.LocalDate; +import java.util.List; +import java.util.Map; + /** * TODO: ДОРАБОТАТЬ в рамках задания * ================================= @@ -7,4 +15,10 @@ package com.example.nto.service; * НЕЛЬЗЯ: Изменять название класса и пакета */ public interface BookingService { + + UserInfoResponse getUserInfo(String code); + + Map> getAvailablePlaces(); + + void createBooking(String code, CreateBookingRequest request); } diff --git a/src/main/java/com/example/nto/service/EmployeeService.java b/src/main/java/com/example/nto/service/EmployeeService.java index cccd209..3953500 100644 --- a/src/main/java/com/example/nto/service/EmployeeService.java +++ b/src/main/java/com/example/nto/service/EmployeeService.java @@ -1,5 +1,9 @@ package com.example.nto.service; +import com.example.nto.entity.Employee; + +import java.util.Optional; + /** * TODO: ДОРАБОТАТЬ в рамках задания * ================================= @@ -7,4 +11,5 @@ package com.example.nto.service; * НЕЛЬЗЯ: Изменять название класса и пакета */ public interface EmployeeService { + Optional findByCode(String code); } diff --git a/src/main/java/com/example/nto/service/impl/BookingServiceImpl.java b/src/main/java/com/example/nto/service/impl/BookingServiceImpl.java index d24b244..c1ad1fa 100644 --- a/src/main/java/com/example/nto/service/impl/BookingServiceImpl.java +++ b/src/main/java/com/example/nto/service/impl/BookingServiceImpl.java @@ -1,12 +1,108 @@ package com.example.nto.service.impl; +import com.example.nto.dto.BookingInfo; +import com.example.nto.dto.CreateBookingRequest; +import com.example.nto.dto.PlaceInfo; +import com.example.nto.dto.UserInfoResponse; +import com.example.nto.entity.Booking; +import com.example.nto.entity.Employee; +import com.example.nto.entity.Place; +import com.example.nto.repository.BookingRepository; +import com.example.nto.repository.EmployeeRepository; +import com.example.nto.repository.PlaceRepository; import com.example.nto.service.BookingService; +import com.example.nto.service.EmployeeService; +import jakarta.persistence.EntityNotFoundException; +import lombok.RequiredArgsConstructor; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.stereotype.Service; -/** - * TODO: ДОРАБОТАТЬ в рамках задания - * ================================= - * МОЖНО: Добавлять методы, аннотации, зависимости - * НЕЛЬЗЯ: Изменять название класса и пакета - */ +import java.time.LocalDate; +import java.util.List; +import java.util.Map; +import java.util.function.Function; +import java.util.stream.Collectors; +import java.util.stream.IntStream; + +@Service +@RequiredArgsConstructor public class BookingServiceImpl implements BookingService { + + private final BookingRepository bookingRepository; + private final PlaceRepository placeRepository; + private final EmployeeService employeeService; + + @Value("${booking.days-ahead}") + private int daysAhead; + + @Override + public UserInfoResponse getUserInfo(String code) { + Employee employee = employeeService.findByCode(code) + .orElseThrow(() -> new EntityNotFoundException("Employee not found with code: " + code)); + + List bookings = bookingRepository.findAllByEmployeeId(employee.getId()); + + Map bookingInfoMap = bookings.stream() + .collect(Collectors.toMap( + Booking::getDate, + booking -> new BookingInfo(booking.getId(), booking.getPlace().getPlace()) + )); + + return new UserInfoResponse(employee.getName(), employee.getPhotoUrl(), bookingInfoMap); + } + + @Override + public Map> getAvailablePlaces() { + LocalDate today = LocalDate.now(); + LocalDate endDate = today.plusDays(daysAhead); + + List allPlaces = placeRepository.findAll(); + List bookings = bookingRepository.findAllByDateBetween(today, endDate); + + Map> bookedPlaceIdsByDate = bookings.stream() + .collect(Collectors.groupingBy( + Booking::getDate, + Collectors.mapping(booking -> booking.getPlace().getId(), Collectors.toList()) + )); + + return IntStream.rangeClosed(0, daysAhead) + .mapToObj(today::plusDays) + .collect(Collectors.toMap( + Function.identity(), + date -> { + List bookedPlaceIds = bookedPlaceIdsByDate.getOrDefault(date, List.of()); + return allPlaces.stream() + .filter(place -> !bookedPlaceIds.contains(place.getId())) + .map(place -> new PlaceInfo(place.getId(), place.getPlace())) + .collect(Collectors.toList()); + } + )); + } + + @Override + public void createBooking(String code, CreateBookingRequest request) { + Employee employee = employeeService.findByCode(code) + .orElseThrow(() -> new EntityNotFoundException("Employee not found with code: " + code)); + + placeRepository.findById(request.getPlaceId()) + .orElseThrow(() -> new EntityNotFoundException("Place not found with id: " + request.getPlaceId())); + + LocalDate today = LocalDate.now(); + LocalDate endDate = today.plusDays(daysAhead); + if (request.getDate().isBefore(today) || request.getDate().isAfter(endDate)) { + throw new IllegalArgumentException("Booking date must be between today and " + endDate); + } + + if (bookingRepository.existsByDateAndPlaceId(request.getDate(), request.getPlaceId())) { + throw new IllegalStateException("Place is already booked on this date"); + } + + Booking booking = Booking.builder() + .date(request.getDate()) + .employee(employee) + .place(Place.builder().id(request.getPlaceId()).build()) + .build(); + + bookingRepository.save(booking); + } } diff --git a/src/main/java/com/example/nto/service/impl/EmployeeServiceImpl.java b/src/main/java/com/example/nto/service/impl/EmployeeServiceImpl.java index f8125e5..ddfc5c4 100644 --- a/src/main/java/com/example/nto/service/impl/EmployeeServiceImpl.java +++ b/src/main/java/com/example/nto/service/impl/EmployeeServiceImpl.java @@ -1,6 +1,12 @@ package com.example.nto.service.impl; +import com.example.nto.entity.Employee; +import com.example.nto.repository.EmployeeRepository; import com.example.nto.service.EmployeeService; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Service; + +import java.util.Optional; /** * TODO: ДОРАБОТАТЬ в рамках задания @@ -8,5 +14,14 @@ import com.example.nto.service.EmployeeService; * МОЖНО: Добавлять методы, аннотации, зависимости * НЕЛЬЗЯ: Изменять название класса и пакета */ +@Service +@RequiredArgsConstructor public class EmployeeServiceImpl implements EmployeeService { + + private final EmployeeRepository employeeRepository; + + @Override + public Optional findByCode(String code) { + return employeeRepository.findByCode(code); + } }