This commit is contained in:
2025-12-04 17:20:04 +06:00
parent 83b3202ea2
commit 7e286a3a7b
22 changed files with 431 additions and 15 deletions

View File

@@ -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);
}
}

View File

@@ -0,0 +1,13 @@
package com.example.nto.DTO;
import lombok.Data;
import lombok.RequiredArgsConstructor;
import java.time.LocalDate;
@Data
@RequiredArgsConstructor
public class BookingDTO {
private Long id;
private PlaceDTO place;
}

View File

@@ -0,0 +1,13 @@
package com.example.nto.DTO;
import lombok.Data;
import lombok.RequiredArgsConstructor;
import java.time.LocalDate;
@Data
@RequiredArgsConstructor
public class BookingRequest {
private LocalDate date;
private Long placeId;
}

View File

@@ -0,0 +1,15 @@
package com.example.nto.DTO;
import lombok.Data;
import lombok.RequiredArgsConstructor;
import java.util.Map;
import java.util.TreeMap;
@Data
@RequiredArgsConstructor
public class EmployeeDTO {
private String name;
private String photoUrl;
private Map<String, BookingDTO> bookings = new TreeMap<>();
}

View File

@@ -0,0 +1,11 @@
package com.example.nto.DTO;
import lombok.Data;
import lombok.RequiredArgsConstructor;
@Data
@RequiredArgsConstructor
public class PlaceDTO {
private Long id;
private String name;
}

View File

@@ -1,10 +1,64 @@
package com.example.nto.controller;
import com.example.nto.DTO.BookingDTO;
import com.example.nto.DTO.BookingRequest;
import com.example.nto.DTO.PlaceDTO;
import com.example.nto.entity.Booking;
import com.example.nto.entity.Place;
import com.example.nto.exceptions.BookingAlreadyExistsException;
import com.example.nto.exceptions.EmployeeNotFoundException;
import com.example.nto.service.BookingService;
import io.swagger.v3.oas.annotations.parameters.RequestBody;
import lombok.RequiredArgsConstructor;
import org.springframework.cglib.core.Local;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
import java.time.LocalDate;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
/**
* TODO: ДОРАБОТАТЬ в рамках задания
* =================================
* МОЖНО: Добавлять методы, аннотации, зависимости
* НЕЛЬЗЯ: Изменять название класса и пакета
*/
@RestController
@RequestMapping("/api/{code}")
@RequiredArgsConstructor
public class BookingController {
private final BookingService bookingService;
@GetMapping("/booking")
public ResponseEntity<Map<LocalDate, List<Place>>> getBooking(@PathVariable String code){
try {
Map<LocalDate, List<Place>> availablePlaces = bookingService.freeBookingPlace(code);
return ResponseEntity.ok(availablePlaces);
} catch (EmployeeNotFoundException e) {
return ResponseEntity.status(HttpStatus.UNAUTHORIZED).build();
} catch (Exception e) {
return ResponseEntity.status(HttpStatus.BAD_GATEWAY).build();
}
}
@PostMapping("/book")
public ResponseEntity<?> createBooking(@PathVariable String code, @RequestBody BookingRequest request){
try {
LocalDate date = request.getDate();
Long placeId = request.getPlaceId();
BookingDTO bookingDTO = bookingService.createBooking(code, date, placeId);
return ResponseEntity.status(HttpStatus.CREATED).build();
} catch (EmployeeNotFoundException e) {
return ResponseEntity.status(HttpStatus.UNAUTHORIZED).build();
} catch (BookingAlreadyExistsException e) {
return ResponseEntity.status(HttpStatus.CONFLICT).build();
} catch (Exception e) {
return ResponseEntity.status(HttpStatus.BAD_GATEWAY).build();
}
}
}

View File

@@ -1,10 +1,57 @@
package com.example.nto.controller;
import com.example.nto.DTO.EmployeeDTO;
import com.example.nto.entity.Booking;
import com.example.nto.entity.Employee;
import com.example.nto.exceptions.EmployeeNotFoundException;
import com.example.nto.service.EmployeeService;
import com.example.nto.utils.MapperEnToDTO;
import lombok.RequiredArgsConstructor;
import org.springframework.http.HttpStatus;
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;
import java.util.*;
/**
* TODO: ДОРАБОТАТЬ в рамках задания
* =================================
* МОЖНО: Добавлять методы, аннотации, зависимости
* НЕЛЬЗЯ: Изменять название класса и пакета
*/
@RestController
@RequestMapping("/api/{code}")
@RequiredArgsConstructor
public class EmployeeController {
private final EmployeeService employeeService;
@GetMapping("/auth")
public ResponseEntity<?> auth(@PathVariable String code){
try {
Employee employee = employeeService.getEmployeeByCode(code);
return ResponseEntity.ok().build();
} catch (EmployeeNotFoundException e) {
return ResponseEntity.status(HttpStatus.UNAUTHORIZED).build();
} catch (Exception e){
return ResponseEntity.status(HttpStatus.BAD_REQUEST).build();
}
}
@GetMapping("/info")
public ResponseEntity<?> info(@PathVariable String code) {
try {
Employee employee = employeeService.getEmployeeByCode(code);
EmployeeDTO response = MapperEnToDTO.toEmployeeInfoDto(employee);
return ResponseEntity.ok(response);
} catch (EmployeeNotFoundException e) {
return ResponseEntity.status(HttpStatus.UNAUTHORIZED).build();
} catch (Exception e) {
return ResponseEntity.status(HttpStatus.BAD_REQUEST).build();
}
}
}

View File

@@ -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;
@@ -17,19 +15,24 @@ import java.time.LocalDate;
* МОЖНО: Добавлять методы, аннотации, зависимости
* НЕЛЬЗЯ: Изменять название класса и пакета
*/
@Entity
@Data
@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.EAGER)
@JoinColumn(name = "employee_id")
private Employee employee;
}

View File

@@ -15,18 +15,21 @@ import java.util.List;
* МОЖНО: Добавлять методы, аннотации, зависимости
* НЕЛЬЗЯ: Изменять название класса и пакета
*/
@Entity
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
@Table(name="employee")
public class Employee {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private long id;
@Column(name="name")
private String name;
@Column(name="code")
private String code;
@Column(name="photoUrl")
private String photoUrl;
@OneToMany(mappedBy = "employee", cascade = CascadeType.ALL, fetch = FetchType.LAZY)

View File

@@ -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;
@@ -15,15 +13,18 @@ import lombok.NoArgsConstructor;
* МОЖНО: Добавлять методы, аннотации, зависимости
* НЕЛЬЗЯ: Изменять название класса и пакета
*/
@Entity
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
@Table(name="place")
public class Place {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private long id;
@Column(name="place_name")
private String place;
}

View File

@@ -0,0 +1,7 @@
package com.example.nto.exceptions;
public class BookingAlreadyExistsException extends RuntimeException {
public BookingAlreadyExistsException(String message) {
super(message);
}
}

View File

@@ -0,0 +1,7 @@
package com.example.nto.exceptions;
public class EmployeeNotFoundException extends RuntimeException {
public EmployeeNotFoundException(String message) {
super(message);
}
}

View File

@@ -0,0 +1,7 @@
package com.example.nto.exceptions;
public class PlaceNotFoundException extends RuntimeException {
public PlaceNotFoundException(String message) {
super(message);
}
}

View File

@@ -0,0 +1,26 @@
package com.example.nto.exceptions.handlers;
import com.example.nto.exceptions.BookingAlreadyExistsException;
import com.example.nto.exceptions.EmployeeNotFoundException;
import com.example.nto.exceptions.PlaceNotFoundException;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseStatus;
@ControllerAdvice
public class GlobalExceptionHandler {
@ExceptionHandler(EmployeeNotFoundException.class)
public ResponseEntity<String> handlerEmployeeNotFoundException(EmployeeNotFoundException e) {
return new ResponseEntity<>(e.getMessage(), HttpStatus.UNAUTHORIZED);
}
@ExceptionHandler(PlaceNotFoundException.class)
public ResponseEntity<String> handlerPlaceNotFoundException(PlaceNotFoundException e){
return new ResponseEntity<>(e.getMessage(), HttpStatus.UNAUTHORIZED);
}
@ExceptionHandler(BookingAlreadyExistsException.class)
public ResponseEntity<String> handlerBookingAlreadyExistsException(BookingAlreadyExistsException e){
return new ResponseEntity<>(e.getMessage(), HttpStatus.CONFLICT);
}
}

View File

@@ -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;
import java.util.Optional;
/**
* TODO: ДОРАБОТАТЬ в рамках задания
* =================================
* МОЖНО: Добавлять методы, аннотации, зависимости
* НЕЛЬЗЯ: Изменять название класса и пакета
*/
public interface BookingRepository {
@Repository
public interface BookingRepository extends JpaRepository<Booking, Long> {
Optional<Booking> findBookingByDate(LocalDate localDate);
List<Booking> findBookingByDateBetween(LocalDate startDate, LocalDate endDate);
Optional<Booking> findBookingByDateAndPlaceId(LocalDate date, Long placeId);
}

View File

@@ -1,10 +1,21 @@
package com.example.nto.repository;
import com.example.nto.entity.Employee;
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.util.Optional;
/**
* TODO: ДОРАБОТАТЬ в рамках задания
* =================================
* МОЖНО: Добавлять методы, аннотации, зависимости
* НЕЛЬЗЯ: Изменять название класса и пакета
*/
public interface EmployeeRepository {
@Repository
public interface EmployeeRepository extends JpaRepository<Employee, Long> {
Optional<Employee> findEmployeeByCode(String code);
}

View File

@@ -1,10 +1,20 @@
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.List;
import java.util.Optional;
/**
* TODO: ДОРАБОТАТЬ в рамках задания
* =================================
* МОЖНО: Добавлять методы, аннотации, зависимости
* НЕЛЬЗЯ: Изменять название класса и пакета
*/
public interface PlaceRepository {
@Repository
public interface PlaceRepository extends JpaRepository<Place, Long> {
Optional<Place> findPlaceById(Long id);
List<Place> findAll();
}

View File

@@ -1,10 +1,24 @@
package com.example.nto.service;
import com.example.nto.DTO.BookingDTO;
import com.example.nto.entity.Booking;
import com.example.nto.entity.Employee;
import com.example.nto.entity.Place;
import org.springframework.stereotype.Service;
import java.time.LocalDate;
import java.util.List;
import java.util.Map;
import java.util.Optional;
/**
* TODO: ДОРАБОТАТЬ в рамках задания
* =================================
* МОЖНО: Добавлять методы, аннотации, зависимости
* НЕЛЬЗЯ: Изменять название класса и пакета
*/
public interface BookingService {
Map<LocalDate, List<Place>> freeBookingPlace(String code);
BookingDTO createBooking(String code, LocalDate date, Long placeId);
}

View File

@@ -1,10 +1,17 @@
package com.example.nto.service;
import com.example.nto.entity.Employee;
import org.springframework.stereotype.Service;
import java.util.Optional;
/**
* TODO: ДОРАБОТАТЬ в рамках задания
* =================================
* МОЖНО: Добавлять методы, аннотации, зависимости
* НЕЛЬЗЯ: Изменять название класса и пакета
*/
public interface EmployeeService {
Employee getEmployeeByCode(String code);
}

View File

@@ -1,6 +1,26 @@
package com.example.nto.service.impl;
import com.example.nto.DTO.BookingDTO;
import com.example.nto.DTO.PlaceDTO;
import com.example.nto.entity.Booking;
import com.example.nto.entity.Employee;
import com.example.nto.entity.Place;
import com.example.nto.exceptions.BookingAlreadyExistsException;
import com.example.nto.exceptions.EmployeeNotFoundException;
import com.example.nto.exceptions.PlaceNotFoundException;
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 com.example.nto.utils.MapperEnToDTO;
import lombok.RequiredArgsConstructor;
import org.springframework.cglib.core.Local;
import org.springframework.stereotype.Service;
import java.time.LocalDate;
import java.util.*;
import java.util.stream.Collectors;
/**
* TODO: ДОРАБОТАТЬ в рамках задания
@@ -8,5 +28,73 @@ import com.example.nto.service.BookingService;
* МОЖНО: Добавлять методы, аннотации, зависимости
* НЕЛЬЗЯ: Изменять название класса и пакета
*/
@Service
@RequiredArgsConstructor
public class BookingServiceImpl implements BookingService {
private static final int DAYS_COUNT = 3;
private final BookingRepository bookingRepository;
private final EmployeeRepository employeeRepository;
private final PlaceRepository placeRepository;
@Override
public Map<LocalDate, List<Place>> freeBookingPlace(String code) {
Optional<Employee> employee = employeeRepository.findEmployeeByCode(code);
if(employee.isEmpty()){
throw new EmployeeNotFoundException("Employee with " + code + " not found!");
}
LocalDate startTime = LocalDate.now();
LocalDate endTime = startTime.plusDays(DAYS_COUNT);
List<Booking> allBookings = bookingRepository.findBookingByDateBetween(startTime, endTime);
List<Place> allPlaces = placeRepository.findAll();
Map<LocalDate, List<Place>> availablePlacesByDate = new HashMap<>();
for (LocalDate date = startTime; !date.isAfter(endTime); date = date.plusDays(1)) {
LocalDate currentDate = date;
Set<Long> bookedPlaceIds = allBookings.stream()
.filter(b -> b.getDate().equals(currentDate))
.map(b -> b.getPlace().getId())
.collect(Collectors.toSet());
List<Place> freePlaces = allPlaces.stream()
.filter(p -> !bookedPlaceIds.contains(p.getId()))
.collect(Collectors.toList());
availablePlacesByDate.put(currentDate, freePlaces);
}
return availablePlacesByDate;
}
@Override
public BookingDTO createBooking(String code, LocalDate date, Long placeId) {
Optional<Employee> employee = employeeRepository.findEmployeeByCode(code);
if(employee.isEmpty()){
throw new EmployeeNotFoundException("Employee with " + code + " not found!");
}
Optional<Place> place = placeRepository.findPlaceById(placeId);
if(place.isEmpty()){
throw new PlaceNotFoundException("Place with " + placeId + " not found!");
}
Optional<Booking> booking = bookingRepository.findBookingByDateAndPlaceId(date, placeId);
if(booking.isPresent()){
throw new BookingAlreadyExistsException("Booking with " + date + " and " + placeId + " already exists!");
}
Booking booking1 = new Booking();
booking1.setDate(date);
booking1.setPlace(place.get());
booking1.setEmployee(employee.get());
return MapperEnToDTO.toBookingInfoDto(bookingRepository.save(booking1));
}
}

View File

@@ -1,6 +1,13 @@
package com.example.nto.service.impl;
import com.example.nto.entity.Employee;
import com.example.nto.exceptions.EmployeeNotFoundException;
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 +15,20 @@ import com.example.nto.service.EmployeeService;
* МОЖНО: Добавлять методы, аннотации, зависимости
* НЕЛЬЗЯ: Изменять название класса и пакета
*/
@Service
@RequiredArgsConstructor
public class EmployeeServiceImpl implements EmployeeService {
private final EmployeeRepository employeeRepository;
@Override
public Employee getEmployeeByCode(String code) {
Optional<Employee> employee = employeeRepository.findEmployeeByCode(code);
if(employee.isEmpty()){
throw new EmployeeNotFoundException("Employee with code " + code + " not found!");
}
return employee.get();
}
}

View File

@@ -0,0 +1,40 @@
package com.example.nto.utils;
import com.example.nto.DTO.BookingDTO;
import com.example.nto.DTO.EmployeeDTO;
import com.example.nto.DTO.PlaceDTO;
import com.example.nto.entity.Booking;
import com.example.nto.entity.Employee;
import com.example.nto.entity.Place;
public class MapperEnToDTO {
public static EmployeeDTO toEmployeeInfoDto(Employee employee) {
EmployeeDTO dto = new EmployeeDTO();
dto.setName(employee.getName());
dto.setPhotoUrl(employee.getPhotoUrl());
if (employee.getBookingList() != null) {
for (Booking booking : employee.getBookingList()) {
BookingDTO bookingDto = toBookingInfoDto(booking);
dto.getBookings().put(booking.getDate().toString(), bookingDto);
}
}
return dto;
}
public static BookingDTO toBookingInfoDto(Booking booking) {
BookingDTO dto = new BookingDTO();
dto.setId(booking.getId());
dto.setPlace(toPlaceDto(booking.getPlace()));
return dto;
}
public static PlaceDTO toPlaceDto(Place place) {
if (place == null) return null;
PlaceDTO dto = new PlaceDTO();
dto.setId(place.getId());
dto.setName(place.getPlace());
return dto;
}
}