почти робит(400 ошибка)

This commit is contained in:
lynxwq2
2025-12-05 15:17:31 +03:00
parent 642eaeaaec
commit 7a0e8b70db
7 changed files with 160 additions and 49 deletions

View File

@@ -1,10 +1,14 @@
package com.example.nto.controller;
import com.example.nto.entity.Booking;
import com.example.nto.exception.BookingConflictException;
import com.example.nto.exception.EmployeeNotFoundException;
import com.example.nto.exception.InvalidBookingException;
import com.example.nto.service.BookingService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.ErrorResponse;
import org.springframework.web.bind.annotation.*;
import java.time.LocalDate;
@@ -45,24 +49,48 @@ public class BookingController {
}
}
public static class CreateBookingRequest {
private LocalDate date;
private long placeId;
@PostMapping("/{code}/book")
public ResponseEntity<?> createBooking(
@PathVariable String code,
@RequestBody Booking.CreateBookingRequest request
) {
try {
Booking booking = bookingService.createBooking(
code,
request.getDate(),
request.getPlaceId()
);
public LocalDate getDate() {
return date;
// Возвращаем только статус 201 без тела
return ResponseEntity.status(HttpStatus.CREATED).build();
} catch (EmployeeNotFoundException e) {
// 401 - кода не существует
return ResponseEntity.status(HttpStatus.UNAUTHORIZED).build();
} catch (BookingConflictException e) {
// 409 - конфликт (уже забронировано)
return ResponseEntity.status(HttpStatus.CONFLICT).build();
} catch (InvalidBookingException e) {
// 400 - невалидные данные
return ResponseEntity.status(HttpStatus.BAD_REQUEST).build();
} catch (Exception e) {
// 500 - внутренняя ошибка сервера
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).build();
}
}
public void setDate(LocalDate date) {
this.date = date;
private Booking.BookingResponse convertToResponse(Booking booking) {
Booking.BookingResponse response = new Booking.BookingResponse();
response.setId(booking.getId());
response.setDate(booking.getDate());
response.setPlaceId(booking.getPlace().getId());
response.setPlaceName(booking.getPlace().getPlace());
response.setEmployeeId(booking.getEmployee().getId());
response.setEmployeeName(booking.getEmployee().getName());
return response;
}
}
public long getPlaceId() {
return placeId;
}
public void setPlaceId(long placeId) {
this.placeId = placeId;
}
}
}

View File

@@ -1,6 +1,7 @@
package com.example.nto.entity;
import com.fasterxml.jackson.annotation.JsonBackReference;
import com.fasterxml.jackson.annotation.JsonFormat;
import com.fasterxml.jackson.annotation.JsonIgnore;
import jakarta.persistence.*;
import lombok.AllArgsConstructor;
@@ -45,4 +46,31 @@ public class Booking {
private long id;
private String place;
}
@Data
public class CreateBookingRequest {
@JsonFormat(pattern = "yyyy-MM-dd")
private LocalDate date;
private long placeId;
}
@Data
public class ErrorResponse {
private String error;
private String message;
public ErrorResponse(String error, String message) {
this.error = error;
this.message = message;
}
}
@Data
public static class BookingResponse {
private Long id;
@JsonFormat(pattern = "yyyy-MM-dd")
private LocalDate date;
private Long placeId;
private String placeName;
private Long employeeId;
private String employeeName;
}
}

View File

@@ -0,0 +1,11 @@
package com.example.nto.exception;
import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.ResponseStatus;
@ResponseStatus(HttpStatus.CONFLICT)
public class BookingConflictException extends RuntimeException {
public BookingConflictException(String message) {
super(message);
}
}

View File

@@ -0,0 +1,12 @@
package com.example.nto.exception;
import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.ResponseStatus;
@ResponseStatus(HttpStatus.NOT_FOUND)
public class EmployeeNotFoundException extends RuntimeException {
public EmployeeNotFoundException(String message) {
super(message);
}
}

View File

@@ -0,0 +1,11 @@
package com.example.nto.exception;
import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.ResponseStatus;
@ResponseStatus(HttpStatus.BAD_REQUEST)
public class InvalidBookingException extends RuntimeException {
public InvalidBookingException(String message) {
super(message);
}
}

View File

@@ -23,9 +23,7 @@ public interface BookingRepository extends JpaRepository<Booking, Long> {
Optional<Booking> findById(Long id);
Optional<Booking> findByDateAndPlace_Id(LocalDate date, Long placeId);
List<Booking> findByDateAndEmployee_Code(LocalDate date, String employeeCode);
List<Booking> findByDateInAndEmployee_Code(List<LocalDate> dates, String employeeCode);
Optional<Booking> findByDateAndPlace_Id(LocalDate date, long placeId);
List<Booking> findByDateAndEmployee_Code(LocalDate date, String employeeCode);
}

View File

@@ -1,8 +1,12 @@
package com.example.nto.service.impl;
import com.example.nto.entity.Booking;
import com.example.nto.entity.Booking.AvailablePlaceDto;
import com.example.nto.entity.Employee;
import com.example.nto.entity.Place;
import com.example.nto.exception.BookingConflictException;
import com.example.nto.exception.EmployeeNotFoundException;
import com.example.nto.exception.InvalidBookingException;
import com.example.nto.repository.BookingRepository;
import com.example.nto.repository.EmployeeRepository;
import com.example.nto.repository.PlaceRepository;
@@ -10,6 +14,7 @@ import com.example.nto.service.BookingService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.time.LocalDate;
import java.util.*;
@@ -49,7 +54,7 @@ public class BookingServiceImpl implements BookingService {
public boolean isEmployeeExists(String code) {
return employeeRepository.findByCode(code).isPresent();
}
public Map<String, List<Booking.AvailablePlaceDto>> getAvailablePlaces(String employeeCode) {
public Map<String, List<AvailablePlaceDto>> getAvailablePlaces(String employeeCode) {
// 1. Получаем сотрудника (для контекста)
Employee employee = employeeRepository.findByCode(employeeCode)
.orElseThrow(() -> new RuntimeException("Employee not found"));
@@ -74,7 +79,7 @@ public class BookingServiceImpl implements BookingService {
.collect(Collectors.groupingBy(Booking::getDate));
// 6. Формируем итоговый ответ
Map<String, List<Booking.AvailablePlaceDto>> result = new HashMap<>();
Map<String, List<AvailablePlaceDto>> result = new HashMap<>();
for (LocalDate date : targetDates) {
String dateKey = date.toString(); // Формат: "yyyy-MM-dd"
@@ -88,10 +93,10 @@ public class BookingServiceImpl implements BookingService {
.collect(Collectors.toSet());
// Фильтруем свободные места
List<Booking.AvailablePlaceDto> available = allPlaces.stream()
List<AvailablePlaceDto> available = allPlaces.stream()
.filter(place -> !occupiedPlaceIds.contains(place.getId()))
.map(place -> {
Booking.AvailablePlaceDto dto = new Booking.AvailablePlaceDto();
AvailablePlaceDto dto = new AvailablePlaceDto();
dto.setId(place.getId());
dto.setPlace(place.getPlace());
return dto;
@@ -104,49 +109,67 @@ public class BookingServiceImpl implements BookingService {
return result;
}
@Override
@Transactional
public Booking createBooking(String employeeCode, LocalDate date, long placeId) {
// Проверяем сотрудника
// 1. Проверяем существование сотрудника (401 если нет)
Employee employee = employeeRepository.findByCode(employeeCode)
.orElseThrow(() -> new RuntimeException("Employee not found with code: " + employeeCode));
.orElseThrow(() -> new EmployeeNotFoundException("Employee with code '" + employeeCode + "' not found"));
// Проверяем место
// 2. Проверяем существование места (400 если нет)
Place place = placeRepository.findById(placeId)
.orElseThrow(() -> new RuntimeException("Place not found with id: " + placeId));
.orElseThrow(() -> new InvalidBookingException("Place with id " + placeId + " not found"));
// Проверяем, что дата в пределах 3 дней от сегодня
LocalDate today = LocalDate.now();
LocalDate maxDate = today.plusDays(3);
// 3. Валидация даты (400 если невалидно)
validateBookingDate(date);
if (date.isBefore(today)) {
throw new RuntimeException("Cannot book in the past. Date: " + date);
}
// 4. Проверяем, не занято ли место (409 если занято)
checkPlaceAvailability(date, placeId);
if (date.isAfter(maxDate)) {
throw new RuntimeException("Can only book up to 3 days in advance. Date: " + date);
}
// 5. Проверяем, нет ли уже брони у сотрудника на эту дату (409 если есть)
checkEmployeeBookingConflict(employeeCode, date);
// Проверяем, не занято ли уже это место на эту дату
Optional<Booking> existingPlaceBooking = bookingRepository.findByDateAndPlace_Id(date, placeId);
if (existingPlaceBooking.isPresent()) {
throw new RuntimeException("Place " + placeId + " is already booked for " + date);
}
// Проверяем, нет ли у сотрудника уже брони на эту дату
List<Booking> employeeBookings = bookingRepository.findByDateAndEmployee_Code(date, employeeCode);
if (!employeeBookings.isEmpty()) {
throw new RuntimeException("Employee already has a booking on " + date);
}
// Создаем новое бронирование
// 6. Создаем и сохраняем бронирование
Booking booking = Booking.builder()
.date(date)
.place(place)
.employee(employee)
.build();
// Сохраняем в базу
return bookingRepository.save(booking);
}
private void validateBookingDate(LocalDate date) {
LocalDate today = LocalDate.now();
LocalDate maxDate = today.plusDays(3);
if (date == null) {
throw new InvalidBookingException("Date is required");
}
if (date.isBefore(today)) {
throw new InvalidBookingException("Cannot book in the past");
}
if (date.isAfter(maxDate)) {
throw new InvalidBookingException("Can only book up to 3 days in advance");
}
}
private void checkPlaceAvailability(LocalDate date, long placeId) {
Optional<Booking> existingBooking = bookingRepository.findByDateAndPlace_Id(date, placeId);
if (existingBooking.isPresent()) {
throw new BookingConflictException("Place " + placeId + " is already booked for " + date);
}
}
private void checkEmployeeBookingConflict(String employeeCode, LocalDate date) {
List<Booking> employeeBookingsOnDate = bookingRepository.findByDateAndEmployee_Code(date, employeeCode);
if (!employeeBookingsOnDate.isEmpty()) {
throw new BookingConflictException("Employee already has a booking on " + date);
}
}
}