This commit is contained in:
v3less11
2025-12-08 21:20:18 +03:00
parent 83b3202ea2
commit d530e32e44
22 changed files with 395 additions and 9 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

@@ -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<Map<String, List<BookingDTO>>> getAvailablePlaces(@PathVariable String code){
return ResponseEntity.ok(bookingService.getAvailablePlaces(code));
}
@PostMapping("/{code}/book")
public ResponseEntity<String> addBooking(@PathVariable String code, @RequestBody BookingCreateDTO bookingCreateDTO){
return ResponseEntity.status(201).body(employeeService.createBooking(code, bookingCreateDTO));
}
}

View File

@@ -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<String> isAuth(@PathVariable String code){
return ResponseEntity.ok(employeeService.employeeAuth(code));
}
@GetMapping("/{code}/info")
public ResponseEntity<EmpInfoDTO> getEmpInfo(@PathVariable String code){
return ResponseEntity.ok(employeeService.getInfoByCode(code));
}
}

View File

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

View File

@@ -0,0 +1,9 @@
package com.example.nto.dto;
import lombok.Data;
@Data
public class BookingDTO {
private long id;
private String place;
}

View File

@@ -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<String, BookingDTO> booking;
}

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

View File

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

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

View File

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

View File

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

View File

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

View File

@@ -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<String> handlerCodeIsNotFoundException(CodeIsNotExistException e){
return new ResponseEntity<>(e.getMessage(), HttpStatus.valueOf(401));
}
@ExceptionHandler(UnknownException.class)
public ResponseEntity<String> handlerUnknownException(UnknownException e){
return new ResponseEntity<>(e.getMessage(), HttpStatus.valueOf(400));
}
@ExceptionHandler(PlaceAlreadyBookedException.class)
public ResponseEntity<String> handlerPlaceAlreadyBookedException(UnknownException e){
return new ResponseEntity<>(e.getMessage(), HttpStatus.valueOf(409));
}
}

View File

@@ -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<Booking, Long> {
boolean existsByDateAndPlaceId(LocalDate date, Long placeId);
@Query("SELECT b.place.id FROM Booking b WHERE b.date = :date")
List<Long> findPlaceIdsByDate(@Param("date") LocalDate date);
}

View File

@@ -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<Employee, Long> {
Optional<Employee> findByCode(String code);
}

View File

@@ -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<Place, Long> {
}

View File

@@ -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<String, List<BookingDTO>> getAvailablePlaces(String code);
}

View File

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

View File

@@ -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<String, List<BookingDTO>> getAvailablePlaces(String code) {
Optional<Employee> optionalEmployee = employeeRepository.findByCode(code);
if (optionalEmployee.isEmpty()) {
throw new CodeIsNotExistException("кода не существует");
}
LocalDate today = LocalDate.now();
List<LocalDate> dates = IntStream.range(0, 4)
.mapToObj(today::plusDays)
.collect(Collectors.toList());
Map<String, List<BookingDTO>> result = new HashMap<>();
for (LocalDate date : dates) {
String dateKey = date.format(DateTimeFormatter.ISO_LOCAL_DATE);
// Все забронированные placeId на эту дату
List<Long> bookedPlaceIds = bookingRepository.findPlaceIdsByDate(date);
// Все доступные места (не забронированные)
List<Place> allPlaces = placeRepository.findAll();
List<Place> availablePlaces = allPlaces.stream()
.filter(place -> !bookedPlaceIds.contains(place.getId()))
.collect(Collectors.toList());
// Преобразуем в BookingSummary
List<BookingDTO> 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;
}
}

View File

@@ -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<Employee> 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<Employee> optionalEmployee = employeeRepository.findByCode(code);
if(optionalEmployee.isEmpty()){
throw new CodeIsNotExistException("кода не существует");
}
return "данный код существует - можно пользоваться приложением";
}
@Override
public String createBooking(String code, BookingCreateDTO bookingCreateDTO) {
Optional<Employee> 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 "бронирование успешно создано";
}
}

View File

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

View File

@@ -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<Booking> → Map<String, BookingSummary>
Map<String, BookingDTO> bookingMap = new HashMap<>();
List<Booking> 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;
}
}