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..b8bbc10 100644 --- a/src/main/java/com/example/nto/controller/BookingController.java +++ b/src/main/java/com/example/nto/controller/BookingController.java @@ -1,10 +1,37 @@ package com.example.nto.controller; +import com.example.nto.dto.BookingCreateDTO; +import com.example.nto.dto.BookingDTO; +import com.example.nto.service.BookingService; +import com.example.nto.service.EmployeeService; +import lombok.RequiredArgsConstructor; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.*; + +import java.util.List; +import java.util.Map; + /** * TODO: ДОРАБОТАТЬ в рамках задания * ================================= * МОЖНО: Добавлять методы, аннотации, зависимости * НЕЛЬЗЯ: Изменять название класса и пакета */ +@RestController +@RequestMapping("/api") +@RequiredArgsConstructor public class BookingController { + + private final EmployeeService employeeService; + private final BookingService bookingService; + + @GetMapping("/{code}/booking") + public ResponseEntity>> getAvailablePlaces(@PathVariable String code){ + return ResponseEntity.ok(bookingService.getAvailablePlaces(code)); + } + + @PostMapping("/{code}/book") + public ResponseEntity addBooking(@PathVariable String code, @RequestBody BookingCreateDTO bookingCreateDTO){ + return ResponseEntity.status(201).body(employeeService.createBooking(code, bookingCreateDTO)); + } } diff --git a/src/main/java/com/example/nto/controller/EmployeeController.java b/src/main/java/com/example/nto/controller/EmployeeController.java index 47658f9..f9a5e52 100644 --- a/src/main/java/com/example/nto/controller/EmployeeController.java +++ b/src/main/java/com/example/nto/controller/EmployeeController.java @@ -1,10 +1,35 @@ package com.example.nto.controller; +import com.example.nto.dto.EmpInfoDTO; +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") +@RequiredArgsConstructor public class EmployeeController { + + private final EmployeeService employeeService; + + @GetMapping("/{code}/auth") + public ResponseEntity isAuth(@PathVariable String code){ + return ResponseEntity.ok(employeeService.employeeAuth(code)); + } + + @GetMapping("/{code}/info") + public ResponseEntity getEmpInfo(@PathVariable String code){ + return ResponseEntity.ok(employeeService.getInfoByCode(code)); + } + } diff --git a/src/main/java/com/example/nto/dto/BookingCreateDTO.java b/src/main/java/com/example/nto/dto/BookingCreateDTO.java new file mode 100644 index 0000000..0c0d5a6 --- /dev/null +++ b/src/main/java/com/example/nto/dto/BookingCreateDTO.java @@ -0,0 +1,11 @@ +package com.example.nto.dto; + +import lombok.Data; + +import java.time.LocalDate; + +@Data +public class BookingCreateDTO { + private LocalDate date; + private long placeId; +} diff --git a/src/main/java/com/example/nto/dto/BookingDTO.java b/src/main/java/com/example/nto/dto/BookingDTO.java new file mode 100644 index 0000000..27bfba7 --- /dev/null +++ b/src/main/java/com/example/nto/dto/BookingDTO.java @@ -0,0 +1,9 @@ +package com.example.nto.dto; + +import lombok.Data; + +@Data +public class BookingDTO { + private long id; + private String place; +} diff --git a/src/main/java/com/example/nto/dto/EmpInfoDTO.java b/src/main/java/com/example/nto/dto/EmpInfoDTO.java new file mode 100644 index 0000000..adfeafa --- /dev/null +++ b/src/main/java/com/example/nto/dto/EmpInfoDTO.java @@ -0,0 +1,12 @@ +package com.example.nto.dto; + +import lombok.Data; + +import java.util.Map; + +@Data +public class EmpInfoDTO { + 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..49316cf 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; @@ -18,18 +16,25 @@ import java.time.LocalDate; * НЕЛЬЗЯ: Изменять название класса и пакета */ @Data +@Entity @Builder @NoArgsConstructor @AllArgsConstructor +@Table(name = "booking") public class Booking { + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) private long id; + @Column(name = "date") private LocalDate date; @ManyToOne(targetEntity = Place.class, fetch = FetchType.LAZY) @JoinColumn(name = "place_id") private Place place; + @ManyToOne(fetch = FetchType.LAZY) + @JoinColumn(name = "employee_id") 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..30a386b 100644 --- a/src/main/java/com/example/nto/entity/Employee.java +++ b/src/main/java/com/example/nto/entity/Employee.java @@ -16,17 +16,24 @@ import java.util.List; * НЕЛЬЗЯ: Изменять название класса и пакета */ @Data +@Entity @Builder @NoArgsConstructor @AllArgsConstructor +@Table(name = "employee") public class Employee { + @Id + @Column(name = "id") private long id; + @Column(name = "name") private String name; + @Column(name = "code") 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..2a83b19 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; @@ -16,14 +14,17 @@ import lombok.NoArgsConstructor; * НЕЛЬЗЯ: Изменять название класса и пакета */ @Data +@Entity @Builder @NoArgsConstructor @AllArgsConstructor +@Table(name = "place") public class Place { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private long id; + @Column(name = "place_name") private String place; } diff --git a/src/main/java/com/example/nto/exception/CodeIsNotExistException.java b/src/main/java/com/example/nto/exception/CodeIsNotExistException.java new file mode 100644 index 0000000..780e071 --- /dev/null +++ b/src/main/java/com/example/nto/exception/CodeIsNotExistException.java @@ -0,0 +1,7 @@ +package com.example.nto.exception; + +public class CodeIsNotExistException extends RuntimeException { + public CodeIsNotExistException(String message) { + super(message); + } +} diff --git a/src/main/java/com/example/nto/exception/PlaceAlreadyBookedException.java b/src/main/java/com/example/nto/exception/PlaceAlreadyBookedException.java new file mode 100644 index 0000000..7d5a16c --- /dev/null +++ b/src/main/java/com/example/nto/exception/PlaceAlreadyBookedException.java @@ -0,0 +1,7 @@ +package com.example.nto.exception; + +public class PlaceAlreadyBookedException extends RuntimeException { + public PlaceAlreadyBookedException(String message) { + super(message); + } +} diff --git a/src/main/java/com/example/nto/exception/UnknownException.java b/src/main/java/com/example/nto/exception/UnknownException.java new file mode 100644 index 0000000..7887f8d --- /dev/null +++ b/src/main/java/com/example/nto/exception/UnknownException.java @@ -0,0 +1,7 @@ +package com.example.nto.exception; + +public class UnknownException extends RuntimeException { + public UnknownException(String message) { + super(message); + } +} diff --git a/src/main/java/com/example/nto/exception/handler/GlobalExceptionHandler.java b/src/main/java/com/example/nto/exception/handler/GlobalExceptionHandler.java new file mode 100644 index 0000000..9f1e56f --- /dev/null +++ b/src/main/java/com/example/nto/exception/handler/GlobalExceptionHandler.java @@ -0,0 +1,29 @@ +package com.example.nto.exception.handler; + +import com.example.nto.exception.CodeIsNotExistException; +import com.example.nto.exception.PlaceAlreadyBookedException; +import com.example.nto.exception.UnknownException; +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(CodeIsNotExistException.class) + public ResponseEntity handlerCodeIsNotFoundException(CodeIsNotExistException e){ + return new ResponseEntity<>(e.getMessage(), HttpStatus.valueOf(401)); + } + + @ExceptionHandler(UnknownException.class) + public ResponseEntity handlerUnknownException(UnknownException e){ + return new ResponseEntity<>(e.getMessage(), HttpStatus.valueOf(400)); + } + + @ExceptionHandler(PlaceAlreadyBookedException.class) + public ResponseEntity handlerPlaceAlreadyBookedException(UnknownException e){ + return new ResponseEntity<>(e.getMessage(), HttpStatus.valueOf(409)); + } + +} diff --git a/src/main/java/com/example/nto/repository/BookingRepository.java b/src/main/java/com/example/nto/repository/BookingRepository.java index 303bb54..117ef5e 100644 --- a/src/main/java/com/example/nto/repository/BookingRepository.java +++ b/src/main/java/com/example/nto/repository/BookingRepository.java @@ -1,10 +1,24 @@ package com.example.nto.repository; +import com.example.nto.entity.Booking; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.jpa.repository.Query; +import org.springframework.data.repository.query.Param; +import org.springframework.stereotype.Repository; + +import java.time.LocalDate; +import java.util.List; + /** * TODO: ДОРАБОТАТЬ в рамках задания * ================================= * МОЖНО: Добавлять методы, аннотации, зависимости * НЕЛЬЗЯ: Изменять название класса и пакета */ -public interface BookingRepository { +@Repository +public interface BookingRepository extends JpaRepository { + boolean existsByDateAndPlaceId(LocalDate date, Long placeId); + + @Query("SELECT b.place.id FROM Booking b WHERE b.date = :date") + List findPlaceIdsByDate(@Param("date") LocalDate date); } diff --git a/src/main/java/com/example/nto/repository/EmployeeRepository.java b/src/main/java/com/example/nto/repository/EmployeeRepository.java index 210d29c..07150fb 100644 --- a/src/main/java/com/example/nto/repository/EmployeeRepository.java +++ b/src/main/java/com/example/nto/repository/EmployeeRepository.java @@ -1,10 +1,16 @@ package com.example.nto.repository; +import com.example.nto.entity.Employee; +import org.springframework.data.jpa.repository.JpaRepository; + +import java.util.Optional; + /** * TODO: ДОРАБОТАТЬ в рамках задания * ================================= * МОЖНО: Добавлять методы, аннотации, зависимости * НЕЛЬЗЯ: Изменять название класса и пакета */ -public interface EmployeeRepository { +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..b55be72 100644 --- a/src/main/java/com/example/nto/repository/PlaceRepository.java +++ b/src/main/java/com/example/nto/repository/PlaceRepository.java @@ -1,10 +1,17 @@ package com.example.nto.repository; +import com.example.nto.entity.Place; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.stereotype.Repository; + +import java.util.Optional; + /** * 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..4dbc80d 100644 --- a/src/main/java/com/example/nto/service/BookingService.java +++ b/src/main/java/com/example/nto/service/BookingService.java @@ -1,5 +1,11 @@ package com.example.nto.service; +import com.example.nto.dto.BookingDTO; +import com.example.nto.entity.Booking; + +import java.util.List; +import java.util.Map; + /** * TODO: ДОРАБОТАТЬ в рамках задания * ================================= @@ -7,4 +13,5 @@ package com.example.nto.service; * НЕЛЬЗЯ: Изменять название класса и пакета */ public interface BookingService { + Map> getAvailablePlaces(String code); } diff --git a/src/main/java/com/example/nto/service/EmployeeService.java b/src/main/java/com/example/nto/service/EmployeeService.java index cccd209..1fc4400 100644 --- a/src/main/java/com/example/nto/service/EmployeeService.java +++ b/src/main/java/com/example/nto/service/EmployeeService.java @@ -1,10 +1,19 @@ package com.example.nto.service; +import com.example.nto.dto.BookingCreateDTO; +import com.example.nto.dto.EmpInfoDTO; + /** * TODO: ДОРАБОТАТЬ в рамках задания * ================================= * МОЖНО: Добавлять методы, аннотации, зависимости * НЕЛЬЗЯ: Изменять название класса и пакета */ + public interface EmployeeService { + EmpInfoDTO getInfoByCode(String code); + + String employeeAuth(String code); + + String createBooking(String code, BookingCreateDTO bookingCreateDTO); } 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..f1e12d6 100644 --- a/src/main/java/com/example/nto/service/impl/BookingServiceImpl.java +++ b/src/main/java/com/example/nto/service/impl/BookingServiceImpl.java @@ -1,6 +1,24 @@ package com.example.nto.service.impl; +import com.example.nto.dto.BookingDTO; +import com.example.nto.entity.Employee; +import com.example.nto.entity.Place; +import com.example.nto.exception.CodeIsNotExistException; +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 lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Service; + +import java.time.LocalDate; +import java.time.format.DateTimeFormatter; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.stream.Collectors; +import java.util.stream.IntStream; /** * TODO: ДОРАБОТАТЬ в рамках задания @@ -8,5 +26,54 @@ import com.example.nto.service.BookingService; * МОЖНО: Добавлять методы, аннотации, зависимости * НЕЛЬЗЯ: Изменять название класса и пакета */ +@Service +@RequiredArgsConstructor public class BookingServiceImpl implements BookingService { + + private final EmployeeRepository employeeRepository; + private final BookingRepository bookingRepository; + private final PlaceRepository placeRepository; + + @Override + public Map> getAvailablePlaces(String code) { + Optional optionalEmployee = employeeRepository.findByCode(code); + + if (optionalEmployee.isEmpty()) { + throw new CodeIsNotExistException("кода не существует"); + } + + LocalDate today = LocalDate.now(); + List dates = IntStream.range(0, 4) + .mapToObj(today::plusDays) + .collect(Collectors.toList()); + + Map> result = new HashMap<>(); + + for (LocalDate date : dates) { + String dateKey = date.format(DateTimeFormatter.ISO_LOCAL_DATE); + + // Все забронированные placeId на эту дату + List bookedPlaceIds = bookingRepository.findPlaceIdsByDate(date); + + // Все доступные места (не забронированные) + List allPlaces = placeRepository.findAll(); + List availablePlaces = allPlaces.stream() + .filter(place -> !bookedPlaceIds.contains(place.getId())) + .collect(Collectors.toList()); + + // Преобразуем в BookingSummary + List dto = availablePlaces.stream() + .map(place -> { + BookingDTO bookingDTO = new BookingDTO(); + bookingDTO.setId(place.getId()); + bookingDTO.setPlace(place.getPlace()); + return bookingDTO; + }) + .collect(Collectors.toList()); + + result.put(dateKey, dto); + } + + return result; + } } 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..c2cf24e 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,21 @@ package com.example.nto.service.impl; +import com.example.nto.dto.BookingCreateDTO; +import com.example.nto.dto.EmpInfoDTO; +import com.example.nto.entity.Booking; +import com.example.nto.entity.Employee; +import com.example.nto.exception.CodeIsNotExistException; +import com.example.nto.exception.PlaceAlreadyBookedException; +import com.example.nto.exception.UnknownException; +import com.example.nto.repository.BookingRepository; +import com.example.nto.repository.EmployeeRepository; +import com.example.nto.repository.PlaceRepository; import com.example.nto.service.EmployeeService; +import com.example.nto.until.EmployeeMapper; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Service; + +import java.util.Optional; /** * TODO: ДОРАБОТАТЬ в рамках задания @@ -8,5 +23,57 @@ import com.example.nto.service.EmployeeService; * МОЖНО: Добавлять методы, аннотации, зависимости * НЕЛЬЗЯ: Изменять название класса и пакета */ +@Service +@RequiredArgsConstructor public class EmployeeServiceImpl implements EmployeeService { + + private final EmployeeRepository employeeRepository; + private final BookingRepository bookingRepository; + private final PlaceRepository placeRepository; + + @Override + public EmpInfoDTO getInfoByCode(String code) { + Optional optionalEmployee = employeeRepository.findByCode(code); + + if(optionalEmployee.isEmpty()){ + throw new CodeIsNotExistException("кода не существует"); + } + + return optionalEmployee.map(EmployeeMapper::convertToDTO).orElseThrow(() -> new UnknownException("что-то пошло не так")); + } + + @Override + public String employeeAuth(String code) { + Optional optionalEmployee = employeeRepository.findByCode(code); + + if(optionalEmployee.isEmpty()){ + throw new CodeIsNotExistException("кода не существует"); + } + + return "данный код существует - можно пользоваться приложением"; + } + + @Override + public String createBooking(String code, BookingCreateDTO bookingCreateDTO) { + Optional optionalEmployee = employeeRepository.findByCode(code); + + if(optionalEmployee.isEmpty()){ + throw new CodeIsNotExistException("кода не существует"); + } + + Employee employee = optionalEmployee.get(); + + if(bookingRepository.existsByDateAndPlaceId(bookingCreateDTO.getDate(), bookingCreateDTO.getPlaceId())){ + throw new PlaceAlreadyBookedException("уже забронировано"); + } + + Booking booking = new Booking(); + booking.setDate(bookingCreateDTO.getDate()); + booking.setEmployee(employee); + booking.setPlace(placeRepository.findById(bookingCreateDTO.getPlaceId()) + .orElseThrow(() -> new UnknownException("что-то пошло не так"))); + + bookingRepository.save(booking); + return "бронирование успешно создано"; + } } diff --git a/src/main/java/com/example/nto/until/BookingMapper.java b/src/main/java/com/example/nto/until/BookingMapper.java new file mode 100644 index 0000000..4dfc8dd --- /dev/null +++ b/src/main/java/com/example/nto/until/BookingMapper.java @@ -0,0 +1,17 @@ +package com.example.nto.until; + +import com.example.nto.dto.BookingCreateDTO; +import com.example.nto.entity.Booking; +import lombok.experimental.UtilityClass; + +@UtilityClass +public class BookingMapper { + public BookingCreateDTO convertToDTO(Booking booking){ + BookingCreateDTO bookingCreateDTO = new BookingCreateDTO(); + + bookingCreateDTO.setDate(booking.getDate()); + bookingCreateDTO.setPlaceId(booking.getPlace().getId()); + + return bookingCreateDTO; + } +} diff --git a/src/main/java/com/example/nto/until/EmployeeMapper.java b/src/main/java/com/example/nto/until/EmployeeMapper.java new file mode 100644 index 0000000..f0da129 --- /dev/null +++ b/src/main/java/com/example/nto/until/EmployeeMapper.java @@ -0,0 +1,40 @@ +package com.example.nto.until; + +import com.example.nto.dto.BookingDTO; +import com.example.nto.dto.EmpInfoDTO; +import com.example.nto.entity.Booking; +import com.example.nto.entity.Employee; +import lombok.experimental.UtilityClass; + +import java.time.format.DateTimeFormatter; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +@UtilityClass +public class EmployeeMapper { + private static final DateTimeFormatter DATE_FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd"); + + public EmpInfoDTO convertToDTO(Employee employee) { + EmpInfoDTO dto = new EmpInfoDTO(); + dto.setName(employee.getName()); + dto.setPhotoUrl(employee.getPhotoUrl()); // ← будет photoUrl в JSON (см. ниже про имя поля) + + // Преобразуем List → Map + Map bookingMap = new HashMap<>(); + List bookings = employee.getBookingList(); + + if (bookings != null) { + for (Booking booking : bookings) { + String dateKey = booking.getDate().format(DATE_FORMATTER); + BookingDTO bookingDTO = new BookingDTO(); + bookingDTO.setId(booking.getId()); + bookingDTO.setPlace(booking.getPlace().getPlace()); + bookingMap.put(dateKey, bookingDTO); + } + } + + dto.setBooking(bookingMap); + return dto; + } +}