From df140765cde5c9efb783379e75dc114789328ded Mon Sep 17 00:00:00 2001 From: Yurchik-gitter Date: Thu, 11 Dec 2025 21:49:48 +0300 Subject: [PATCH] 1 --- src/main/java/com/example/nto/App.java | 11 ++- .../nto/controller/BookingController.java | 88 +++++++++++++++++-- .../nto/controller/EmployeeController.java | 77 ++++++++++++++-- .../java/com/example/nto/entity/Booking.java | 74 +++++++++++----- .../java/com/example/nto/entity/Employee.java | 39 ++++---- .../java/com/example/nto/entity/Place.java | 32 +++---- .../nto/repository/BookingRepository.java | 22 +++-- .../nto/repository/EmployeeRepository.java | 15 ++-- .../nto/repository/PlaceRepository.java | 11 +-- .../example/nto/service/BookingService.java | 14 +-- .../example/nto/service/EmployeeService.java | 11 ++- .../nto/service/impl/BookingServiceImpl.java | 74 ++++++++++++++-- .../nto/service/impl/EmployeeServiceImpl.java | 24 +++-- 13 files changed, 371 insertions(+), 121 deletions(-) diff --git a/src/main/java/com/example/nto/App.java b/src/main/java/com/example/nto/App.java index e453f89..d4add94 100644 --- a/src/main/java/com/example/nto/App.java +++ b/src/main/java/com/example/nto/App.java @@ -1,12 +1,11 @@ package com.example.nto; -/** - * TODO: ДОРАБОТАТЬ в рамках задания - * ================================= - * МОЖНО: Добавлять методы, аннотации, зависимости - * НЕЛЬЗЯ: Изменять название класса и пакета - */ +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; + +@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..7f6bdd9 100644 --- a/src/main/java/com/example/nto/controller/BookingController.java +++ b/src/main/java/com/example/nto/controller/BookingController.java @@ -1,10 +1,86 @@ package com.example.nto.controller; -/** - * TODO: ДОРАБОТАТЬ в рамках задания - * ================================= - * МОЖНО: Добавлять методы, аннотации, зависимости - * НЕЛЬЗЯ: Изменять название класса и пакета - */ +import com.example.nto.dto.BookingRequestDto; +import com.example.nto.dto.FreePlaceDto; +import com.example.nto.entity.Place; +import com.example.nto.repository.EmployeeRepository; +import com.example.nto.service.BookingService; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.*; + +import java.time.LocalDate; +import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; + +@RestController +@RequestMapping("/api/{code}") public class BookingController { + + private final BookingService service; + private final EmployeeRepository employeeRepository; + + public BookingController(BookingService service, EmployeeRepository employeeRepository) { + this.service = service; + this.employeeRepository = employeeRepository; + } + + private boolean isValidCode(String code) { + return code != null && code.matches("\\d+"); + } + + @GetMapping("/booking") + public ResponseEntity getFree(@PathVariable String code) { + if (!isValidCode(code)) { + return ResponseEntity.status(400).body("Bad request"); + } + + if (employeeRepository.findByCode(code).isEmpty()) { + return ResponseEntity.status(401).body("Unauthorized"); + } + + Map> free = service.getAvailablePlacesForRange(LocalDate.now(), 4); + + Map> dto = free.entrySet().stream() + .collect(Collectors.toMap( + e -> e.getKey().toString(), + e -> e.getValue().stream() + .map(p -> new FreePlaceDto(p.getId(), p.getPlace())) + .collect(Collectors.toList()) + )); + + return ResponseEntity.ok(dto); + } + + @PostMapping("/book") + public ResponseEntity book(@PathVariable String code, + @RequestBody BookingRequestDto dto) { + if (!isValidCode(code)) { + return ResponseEntity.status(400).body("Bad request"); + } + + if (employeeRepository.findByCode(code).isEmpty()) { + return ResponseEntity.status(401).body("Unauthorized"); + } + + try { + service.createBooking(code, LocalDate.parse(dto.getDate()), dto.getPlaceId()); + return ResponseEntity.status(201).body("Booking created"); + } catch (IllegalArgumentException ex) { + String msg = ex.getMessage(); + if ("Place not found".equals(msg)) { + return ResponseEntity.status(400).body("Bad request"); + } + return ResponseEntity.status(400).body("Bad request"); + } catch (IllegalStateException ex) { + return ResponseEntity.status(409).body("Conflict"); + } catch (Exception ex) { + return ResponseEntity.status(400).body("Bad request"); + } + } + + @ExceptionHandler({org.springframework.http.converter.HttpMessageNotReadableException.class}) + public ResponseEntity handleBadRequest() { + return ResponseEntity.status(400).body("Bad request"); + } } diff --git a/src/main/java/com/example/nto/controller/EmployeeController.java b/src/main/java/com/example/nto/controller/EmployeeController.java index 47658f9..fc14aa1 100644 --- a/src/main/java/com/example/nto/controller/EmployeeController.java +++ b/src/main/java/com/example/nto/controller/EmployeeController.java @@ -1,10 +1,75 @@ package com.example.nto.controller; -/** - * TODO: ДОРАБОТАТЬ в рамках задания - * ================================= - * МОЖНО: Добавлять методы, аннотации, зависимости - * НЕЛЬЗЯ: Изменять название класса и пакета - */ +import com.example.nto.dto.BookingInfoDto; +import com.example.nto.dto.EmployeeInfoDto; +import com.example.nto.entity.Booking; +import com.example.nto.entity.Employee; +import com.example.nto.repository.EmployeeRepository; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.*; +import org.springframework.http.converter.HttpMessageNotReadableException; + +import java.util.LinkedHashMap; +import java.util.Map; + +@RestController +@RequestMapping("/api/{code}") public class EmployeeController { + + private final EmployeeRepository employeeRepository; + + public EmployeeController(EmployeeRepository employeeRepository) { + this.employeeRepository = employeeRepository; + } + + private boolean isCodeFormatValid(String code) { + return code != null && code.matches("\\d+"); + } + + @GetMapping("/auth") + public ResponseEntity auth(@PathVariable String code) { + if (!isCodeFormatValid(code)) { + return ResponseEntity.status(400).body("Bad request"); + } + + boolean exists = employeeRepository.findByCode(code).isPresent(); + if (!exists) { + return ResponseEntity.status(401).body("Unauthorized"); + } + + return ResponseEntity.ok().build(); + } + + @GetMapping("/info") + public ResponseEntity info(@PathVariable String code) { + if (!isCodeFormatValid(code)) { + return ResponseEntity.status(400).body("Bad request"); + } + + Employee employee = employeeRepository.findByCode(code).orElse(null); + if (employee == null) { + return ResponseEntity.status(401).body("Unauthorized"); + } + + Map bookingMap = new LinkedHashMap<>(); + for (Booking b : employee.getBookingList()) { + bookingMap.put( + b.getDate().toString(), + new BookingInfoDto(b.getId(), b.getPlace().getPlace()) + ); + } + + EmployeeInfoDto dto = new EmployeeInfoDto( + employee.getName(), + employee.getPhotoUrl(), + bookingMap + ); + + return ResponseEntity.ok(dto); + } + + @ExceptionHandler({IllegalArgumentException.class, HttpMessageNotReadableException.class}) + public ResponseEntity handleBadRequest() { + return ResponseEntity.status(400).body("Bad request"); + } } diff --git a/src/main/java/com/example/nto/entity/Booking.java b/src/main/java/com/example/nto/entity/Booking.java index 21c1981..693c852 100644 --- a/src/main/java/com/example/nto/entity/Booking.java +++ b/src/main/java/com/example/nto/entity/Booking.java @@ -1,35 +1,65 @@ package com.example.nto.entity; -import jakarta.persistence.FetchType; -import jakarta.persistence.JoinColumn; -import jakarta.persistence.ManyToOne; -import lombok.AllArgsConstructor; -import lombok.Builder; -import lombok.Data; -import lombok.NoArgsConstructor; - +import jakarta.persistence.*; import java.time.LocalDate; - -/** - * TODO: ДОРАБОТАТЬ в рамках задания - * ================================= - * МОЖНО: Добавлять методы, аннотации, зависимости - * НЕЛЬЗЯ: Изменять название класса и пакета - */ -@Data -@Builder -@NoArgsConstructor -@AllArgsConstructor +@Entity +@Table(name = "booking") public class Booking { - private long id; + @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.EAGER, optional = false) + @JoinColumn(name = "place_id", nullable = false) private Place place; + @ManyToOne(fetch = FetchType.LAZY, optional = false) + @JoinColumn(name = "employee_id", nullable = false) private Employee employee; + + public Booking() { + } + + public Booking(LocalDate date, Place place, Employee employee) { + this.date = date; + this.place = place; + this.employee = employee; + } + + public Long getId() { + return id; + } + + public LocalDate getDate() { + return date; + } + + public Place getPlace() { + return place; + } + + public Employee getEmployee() { + return employee; + } + + public void setId(Long id) { + this.id = id; + } + + public void setDate(LocalDate date) { + this.date = date; + } + + public void setPlace(Place place) { + this.place = place; + } + + public void setEmployee(Employee employee) { + this.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..00f6d9f 100644 --- a/src/main/java/com/example/nto/entity/Employee.java +++ b/src/main/java/com/example/nto/entity/Employee.java @@ -1,34 +1,39 @@ package com.example.nto.entity; import jakarta.persistence.*; -import lombok.AllArgsConstructor; -import lombok.Builder; -import lombok.Data; -import lombok.NoArgsConstructor; - import java.util.List; - -/** - * TODO: ДОРАБОТАТЬ в рамках задания - * ================================= - * МОЖНО: Добавлять методы, аннотации, зависимости - * НЕЛЬЗЯ: Изменять название класса и пакета - */ -@Data -@Builder -@NoArgsConstructor -@AllArgsConstructor +@Entity +@Table(name = "employee") public class Employee { - private long id; + @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) private List bookingList; + + public Employee() {} + + public Long getId() { return id; } + public String getName() { return name; } + public String getCode() { return code; } + public String getPhotoUrl() { return photoUrl; } + public List getBookingList() { return bookingList; } + + public void setId(Long id) { this.id = id; } + public void setName(String name) { this.name = name; } + public void setCode(String code) { this.code = code; } + public void setPhotoUrl(String photoUrl) { this.photoUrl = photoUrl; } + public void setBookingList(List bookingList) { this.bookingList = bookingList; } } diff --git a/src/main/java/com/example/nto/entity/Place.java b/src/main/java/com/example/nto/entity/Place.java index 00c253b..be849e5 100644 --- a/src/main/java/com/example/nto/entity/Place.java +++ b/src/main/java/com/example/nto/entity/Place.java @@ -1,29 +1,23 @@ package com.example.nto.entity; -import jakarta.persistence.GeneratedValue; -import jakarta.persistence.GenerationType; -import jakarta.persistence.Id; -import lombok.AllArgsConstructor; -import lombok.Builder; -import lombok.Data; -import lombok.NoArgsConstructor; +import jakarta.persistence.*; - -/** - * TODO: ДОРАБОТАТЬ в рамках задания - * ================================= - * МОЖНО: Добавлять методы, аннотации, зависимости - * НЕЛЬЗЯ: Изменять название класса и пакета - */ -@Data -@Builder -@NoArgsConstructor -@AllArgsConstructor +@Entity +@Table(name = "place") public class Place { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) - private long id; + private Long id; + @Column(name = "place_name", nullable = false) private String place; + + public Place() {} + + public Long getId() { return id; } + public String getPlace() { return place; } + + public void setId(Long id) { this.id = id; } + public void setPlace(String place) { this.place = 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..5fd77cd 100644 --- a/src/main/java/com/example/nto/repository/BookingRepository.java +++ b/src/main/java/com/example/nto/repository/BookingRepository.java @@ -1,10 +1,18 @@ package com.example.nto.repository; -/** - * TODO: ДОРАБОТАТЬ в рамках задания - * ================================= - * МОЖНО: Добавлять методы, аннотации, зависимости - * НЕЛЬЗЯ: Изменять название класса и пакета - */ -public interface BookingRepository { +import com.example.nto.entity.Booking; +import com.example.nto.entity.Place; +import org.springframework.data.jpa.repository.JpaRepository; + +import java.time.LocalDate; +import java.util.List; +import java.util.Optional; + +public interface BookingRepository extends JpaRepository { + + List findByDate(LocalDate date); + + Optional findByPlaceAndDate(Place place, LocalDate date); + + List findByEmployeeId(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..9c8155b 100644 --- a/src/main/java/com/example/nto/repository/EmployeeRepository.java +++ b/src/main/java/com/example/nto/repository/EmployeeRepository.java @@ -1,10 +1,11 @@ package com.example.nto.repository; -/** - * TODO: ДОРАБОТАТЬ в рамках задания - * ================================= - * МОЖНО: Добавлять методы, аннотации, зависимости - * НЕЛЬЗЯ: Изменять название класса и пакета - */ -public interface EmployeeRepository { +import com.example.nto.entity.Employee; +import org.springframework.data.jpa.repository.JpaRepository; + +import java.util.Optional; + +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..e7b84c9 100644 --- a/src/main/java/com/example/nto/repository/PlaceRepository.java +++ b/src/main/java/com/example/nto/repository/PlaceRepository.java @@ -1,10 +1,7 @@ package com.example.nto.repository; -/** - * TODO: ДОРАБОТАТЬ в рамках задания - * ================================= - * МОЖНО: Добавлять методы, аннотации, зависимости - * НЕЛЬЗЯ: Изменять название класса и пакета - */ -public interface PlaceRepository { +import com.example.nto.entity.Place; +import org.springframework.data.jpa.repository.JpaRepository; + +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..c996257 100644 --- a/src/main/java/com/example/nto/service/BookingService.java +++ b/src/main/java/com/example/nto/service/BookingService.java @@ -1,10 +1,12 @@ package com.example.nto.service; -/** - * TODO: ДОРАБОТАТЬ в рамках задания - * ================================= - * МОЖНО: Добавлять методы, аннотации, зависимости - * НЕЛЬЗЯ: Изменять название класса и пакета - */ +import com.example.nto.entity.Place; + +import java.time.LocalDate; +import java.util.List; +import java.util.Map; + public interface BookingService { + Map> getAvailablePlacesForRange(LocalDate start, int days); + void createBooking(String code, LocalDate date, Long placeId); } diff --git a/src/main/java/com/example/nto/service/EmployeeService.java b/src/main/java/com/example/nto/service/EmployeeService.java index cccd209..9c8432a 100644 --- a/src/main/java/com/example/nto/service/EmployeeService.java +++ b/src/main/java/com/example/nto/service/EmployeeService.java @@ -1,10 +1,9 @@ package com.example.nto.service; -/** - * TODO: ДОРАБОТАТЬ в рамках задания - * ================================= - * МОЖНО: Добавлять методы, аннотации, зависимости - * НЕЛЬЗЯ: Изменять название класса и пакета - */ +import com.example.nto.entity.Employee; + +import java.util.Optional; + 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..713a356 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,74 @@ package com.example.nto.service.impl; +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 org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; -/** - * TODO: ДОРАБОТАТЬ в рамках задания - * ================================= - * МОЖНО: Добавлять методы, аннотации, зависимости - * НЕЛЬЗЯ: Изменять название класса и пакета - */ +import java.time.LocalDate; +import java.util.*; +import java.util.stream.Collectors; + +@Service public class BookingServiceImpl implements BookingService { + + private final BookingRepository bookingRepository; + private final PlaceRepository placeRepository; + private final EmployeeRepository employeeRepository; + + public BookingServiceImpl(BookingRepository bookingRepository, + PlaceRepository placeRepository, + EmployeeRepository employeeRepository) { + this.bookingRepository = bookingRepository; + this.placeRepository = placeRepository; + this.employeeRepository = employeeRepository; + } + + @Override + public Map> getAvailablePlacesForRange(LocalDate start, int days) { + Map> result = new LinkedHashMap<>(); + List allPlaces = placeRepository.findAll(); + + for (int i = 0; i < days; i++) { + LocalDate date = start.plusDays(i); + List booked = bookingRepository.findByDate(date); + Set bookedIds = booked.stream() + .map(b -> b.getPlace().getId()) + .collect(Collectors.toSet()); + + List free = allPlaces.stream() + .filter(p -> !bookedIds.contains(p.getId())) + .collect(Collectors.toList()); + + result.put(date, free); + } + return result; + } + + @Override + @Transactional + public void createBooking(String code, LocalDate date, Long placeId) { + // Проверка существования сотрудника + Employee employee = employeeRepository.findByCode(code) + .orElseThrow(() -> new IllegalArgumentException("Employee not found")); + + // Проверка существования места + Place place = placeRepository.findById(placeId) + .orElseThrow(() -> new IllegalArgumentException("Place not found")); + + // Проверка, что место уже не забронировано на эту дату + boolean alreadyBooked = bookingRepository.findByPlaceAndDate(place, date).isPresent(); + if (alreadyBooked) { + throw new IllegalStateException("Place already booked"); + } + + // Создание и сохранение нового бронирования + Booking booking = new Booking(date, place, employee); + 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..c09c994 100644 --- a/src/main/java/com/example/nto/service/impl/EmployeeServiceImpl.java +++ b/src/main/java/com/example/nto/service/impl/EmployeeServiceImpl.java @@ -1,12 +1,24 @@ 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 org.springframework.stereotype.Service; -/** - * TODO: ДОРАБОТАТЬ в рамках задания - * ================================= - * МОЖНО: Добавлять методы, аннотации, зависимости - * НЕЛЬЗЯ: Изменять название класса и пакета - */ +import java.util.Optional; + +@Service public class EmployeeServiceImpl implements EmployeeService { + + private final EmployeeRepository employeeRepository; + + public EmployeeServiceImpl(EmployeeRepository employeeRepository) { + this.employeeRepository = employeeRepository; + } + + @Override + public Optional findByCode(String code) { + if (code == null) return Optional.empty(); + return employeeRepository.findByCode(code); + } }