From c9c12e24815b3b484550640c029394e40d32e41c Mon Sep 17 00:00:00 2001 From: Yurchik-gitter Date: Wed, 3 Dec 2025 18:19:50 +0300 Subject: [PATCH] first commit --- src/main/java/com/example/nto/App.java | 11 ++- .../nto/controller/BookingController.java | 84 +++++++++++++++++-- .../nto/controller/EmployeeController.java | 70 ++++++++++++++-- .../java/com/example/nto/entity/Booking.java | 16 ++-- .../java/com/example/nto/entity/Employee.java | 11 +-- .../java/com/example/nto/entity/Place.java | 10 +-- .../nto/repository/BookingRepository.java | 20 +++-- .../nto/repository/EmployeeRepository.java | 16 ++-- .../nto/repository/PlaceRepository.java | 13 ++- .../example/nto/service/BookingService.java | 16 ++-- .../example/nto/service/EmployeeService.java | 11 ++- .../nto/service/impl/BookingServiceImpl.java | 76 +++++++++++++++-- .../nto/service/impl/EmployeeServiceImpl.java | 24 ++++-- 13 files changed, 297 insertions(+), 81 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..91d0a0e 100644 --- a/src/main/java/com/example/nto/controller/BookingController.java +++ b/src/main/java/com/example/nto/controller/BookingController.java @@ -1,10 +1,82 @@ package com.example.nto.controller; -/** - * TODO: ДОРАБОТАТЬ в рамках задания - * ================================= - * МОЖНО: Добавлять методы, аннотации, зависимости - * НЕЛЬЗЯ: Изменять название класса и пакета - */ +import com.example.nto.entity.Booking; +import com.example.nto.entity.Place; +import com.example.nto.entity.Employee; +import com.example.nto.service.BookingService; +import com.example.nto.service.EmployeeService; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.*; + +import java.time.LocalDate; +import java.util.*; +import java.util.stream.Collectors; + +@RestController public class BookingController { + + private final BookingService bookingService; + private final EmployeeService employeeService; + + public BookingController(BookingService bookingService, EmployeeService employeeService) { + this.bookingService = bookingService; + this.employeeService = employeeService; + } + + // GET /api/{code}/booking -> available places for current + 3 days (4 days total) + @GetMapping("/api/{code}/booking") + public ResponseEntity getAvailable(@PathVariable("code") String code) { + try { + if (code == null) return ResponseEntity.badRequest().build(); + Optional e = employeeService.findByCode(code); + if (e.isEmpty()) return ResponseEntity.status(HttpStatus.UNAUTHORIZED).build(); + LocalDate today = LocalDate.now(); + Map> map = bookingService.getAvailablePlacesForRange(today, 4); + // convert dates to string keys and places to required JSON objects {id, place} + Map>> out = new LinkedHashMap<>(); + map.forEach((date, places) -> { + List> list = places.stream().map(p -> { + Map m = new LinkedHashMap<>(); + m.put("id", p.getId()); + m.put("place", p.getPlace()); + return m; + }).collect(Collectors.toList()); + out.put(date.toString(), list); + }); + return ResponseEntity.ok(out); + } catch (Exception ex) { + return ResponseEntity.badRequest().build(); + } + } + + // POST /api/{code}/book + @PostMapping("/api/{code}/book") + public ResponseEntity book(@PathVariable("code") String code, @RequestBody Map body) { + try { + if (code == null) return ResponseEntity.badRequest().build(); + Optional eOpt = employeeService.findByCode(code); + if (eOpt.isEmpty()) return ResponseEntity.status(HttpStatus.UNAUTHORIZED).build(); + Employee e = eOpt.get(); + + if (!body.containsKey("date") || !body.containsKey("placeId")) { + return ResponseEntity.badRequest().build(); + } + String dateStr = body.get("date").toString(); + LocalDate date = LocalDate.parse(dateStr); + long placeId = Long.parseLong(body.get("placeId").toString()); + + try { + Booking booking = bookingService.createBooking(e.getId(), placeId, date); + return ResponseEntity.status(HttpStatus.CREATED).build(); + } catch (IllegalStateException ex) { + if (ex.getMessage() != null && ex.getMessage().toLowerCase().contains("already")) { + return ResponseEntity.status(HttpStatus.CONFLICT).build(); + } + return ResponseEntity.badRequest().build(); + } + } catch (Exception ex) { + return ResponseEntity.badRequest().build(); + } + } } diff --git a/src/main/java/com/example/nto/controller/EmployeeController.java b/src/main/java/com/example/nto/controller/EmployeeController.java index 47658f9..c4e8a6b 100644 --- a/src/main/java/com/example/nto/controller/EmployeeController.java +++ b/src/main/java/com/example/nto/controller/EmployeeController.java @@ -1,10 +1,68 @@ package com.example.nto.controller; -/** - * TODO: ДОРАБОТАТЬ в рамках задания - * ================================= - * МОЖНО: Добавлять методы, аннотации, зависимости - * НЕЛЬЗЯ: Изменять название класса и пакета - */ +import com.example.nto.entity.Booking; +import com.example.nto.entity.Employee; +import com.example.nto.service.BookingService; +import com.example.nto.service.EmployeeService; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.*; + +import java.util.*; +import java.util.stream.Collectors; + +@RestController public class EmployeeController { + + private final EmployeeService employeeService; + private final BookingService bookingService; + + public EmployeeController(EmployeeService employeeService, BookingService bookingService) { + this.employeeService = employeeService; + this.bookingService = bookingService; + } + + // GET /api/{code}/auth + @GetMapping("/api/{code}/auth") + public ResponseEntity auth(@PathVariable("code") String code) { + try { + if (code == null) return ResponseEntity.badRequest().build(); + Optional e = employeeService.findByCode(code); + return e.isPresent() ? ResponseEntity.ok().build() : ResponseEntity.status(HttpStatus.UNAUTHORIZED).build(); + } catch (Exception ex) { + return ResponseEntity.badRequest().build(); + } + } + + // GET /api/{code}/info + @GetMapping("/api/{code}/info") + public ResponseEntity info(@PathVariable("code") String code) { + try { + if (code == null) return ResponseEntity.badRequest().build(); + Optional opt = employeeService.findByCode(code); + if (opt.isEmpty()) return ResponseEntity.status(HttpStatus.UNAUTHORIZED).build(); + Employee e = opt.get(); + Map body = new LinkedHashMap<>(); + body.put("name", e.getName()); + body.put("photoUrl", e.getPhotoUrl()); + // bookings grouped by date + List bookings = bookingService.getBookingsForEmployee(e.getId()); + Map> bookingMap = bookings.stream() + .collect(Collectors.toMap( + b -> b.getDate().toString(), + b -> { + Map m = new LinkedHashMap<>(); + m.put("id", b.getId()); + m.put("place", b.getPlace().getPlace()); + return m; + }, + (a,b)->a, + LinkedHashMap::new + )); + body.put("booking", bookingMap); + return ResponseEntity.ok(body); + } catch (Exception ex) { + return ResponseEntity.badRequest().build(); + } + } } diff --git a/src/main/java/com/example/nto/entity/Booking.java b/src/main/java/com/example/nto/entity/Booking.java index 21c1981..7ebc0bf 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; @@ -10,19 +8,19 @@ import lombok.NoArgsConstructor; import java.time.LocalDate; - /** - * TODO: ДОРАБОТАТЬ в рамках задания - * ================================= - * МОЖНО: Добавлять методы, аннотации, зависимости - * НЕЛЬЗЯ: Изменять название класса и пакета + * Booking entity */ +@Entity +@Table(name = "booking") @Data @Builder @NoArgsConstructor @AllArgsConstructor public class Booking { + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) private long id; private LocalDate date; @@ -31,5 +29,7 @@ public class Booking { @JoinColumn(name = "place_id") private Place place; + @ManyToOne(targetEntity = Employee.class, 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..e182ac7 100644 --- a/src/main/java/com/example/nto/entity/Employee.java +++ b/src/main/java/com/example/nto/entity/Employee.java @@ -8,23 +8,24 @@ import lombok.NoArgsConstructor; import java.util.List; - /** - * TODO: ДОРАБОТАТЬ в рамках задания - * ================================= - * МОЖНО: Добавлять методы, аннотации, зависимости - * НЕЛЬЗЯ: Изменять название класса и пакета + * Employee entity */ +@Entity +@Table(name = "employee") @Data @Builder @NoArgsConstructor @AllArgsConstructor public class Employee { + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) private long id; private String name; + @Column(unique = true, nullable = false) private String code; private String photoUrl; diff --git a/src/main/java/com/example/nto/entity/Place.java b/src/main/java/com/example/nto/entity/Place.java index 00c253b..eff4580 100644 --- a/src/main/java/com/example/nto/entity/Place.java +++ b/src/main/java/com/example/nto/entity/Place.java @@ -1,20 +1,20 @@ package com.example.nto.entity; +import jakarta.persistence.Entity; import jakarta.persistence.GeneratedValue; import jakarta.persistence.GenerationType; import jakarta.persistence.Id; +import jakarta.persistence.Table; import lombok.AllArgsConstructor; import lombok.Builder; import lombok.Data; import lombok.NoArgsConstructor; - /** - * TODO: ДОРАБОТАТЬ в рамках задания - * ================================= - * МОЖНО: Добавлять методы, аннотации, зависимости - * НЕЛЬЗЯ: Изменять название класса и пакета + * Place entity */ +@Entity +@Table(name = "place") @Data @Builder @NoArgsConstructor diff --git a/src/main/java/com/example/nto/repository/BookingRepository.java b/src/main/java/com/example/nto/repository/BookingRepository.java index 303bb54..9cdb5b3 100644 --- a/src/main/java/com/example/nto/repository/BookingRepository.java +++ b/src/main/java/com/example/nto/repository/BookingRepository.java @@ -1,10 +1,16 @@ 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 org.springframework.stereotype.Repository; + +import java.time.LocalDate; +import java.util.List; + +@Repository +public interface BookingRepository extends JpaRepository { + List findByDate(LocalDate date); + List 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..894758e 100644 --- a/src/main/java/com/example/nto/repository/EmployeeRepository.java +++ b/src/main/java/com/example/nto/repository/EmployeeRepository.java @@ -1,10 +1,12 @@ package com.example.nto.repository; -/** - * TODO: ДОРАБОТАТЬ в рамках задания - * ================================= - * МОЖНО: Добавлять методы, аннотации, зависимости - * НЕЛЬЗЯ: Изменять название класса и пакета - */ -public interface EmployeeRepository { +import com.example.nto.entity.Employee; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.stereotype.Repository; + +import java.util.Optional; + +@Repository +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..2384391 100644 --- a/src/main/java/com/example/nto/repository/PlaceRepository.java +++ b/src/main/java/com/example/nto/repository/PlaceRepository.java @@ -1,10 +1,9 @@ package com.example.nto.repository; -/** - * TODO: ДОРАБОТАТЬ в рамках задания - * ================================= - * МОЖНО: Добавлять методы, аннотации, зависимости - * НЕЛЬЗЯ: Изменять название класса и пакета - */ -public interface PlaceRepository { +import com.example.nto.entity.Place; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.stereotype.Repository; + +@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..01eb387 100644 --- a/src/main/java/com/example/nto/service/BookingService.java +++ b/src/main/java/com/example/nto/service/BookingService.java @@ -1,10 +1,14 @@ package com.example.nto.service; -/** - * TODO: ДОРАБОТАТЬ в рамках задания - * ================================= - * МОЖНО: Добавлять методы, аннотации, зависимости - * НЕЛЬЗЯ: Изменять название класса и пакета - */ +import com.example.nto.entity.Booking; +import com.example.nto.entity.Place; + +import java.time.LocalDate; +import java.util.List; +import java.util.Map; + public interface BookingService { + Map> getAvailablePlacesForRange(LocalDate fromInclusive, int days); + Booking createBooking(long employeeId, long placeId, LocalDate date) throws IllegalStateException; + List getBookingsForEmployee(long employeeId); } 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..8f35655 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,76 @@ package com.example.nto.service.impl; +import com.example.nto.entity.Booking; +import com.example.nto.entity.Place; +import com.example.nto.entity.Employee; +import com.example.nto.repository.BookingRepository; +import com.example.nto.repository.PlaceRepository; +import com.example.nto.repository.EmployeeRepository; 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 fromInclusive, int days) { + List allPlaces = placeRepository.findAll(); + Map> result = new LinkedHashMap<>(); + for (int i = 0; i < days; i++) { + LocalDate date = fromInclusive.plusDays(i); + List bookedPlaceIds = bookingRepository.findByDate(date).stream() + .map(b -> b.getPlace().getId()).collect(Collectors.toList()); + List free = allPlaces.stream() + .filter(p -> !bookedPlaceIds.contains(p.getId())) + .collect(Collectors.toList()); + result.put(date, free); + } + return result; + } + + @Override + @Transactional + public Booking createBooking(long employeeId, long placeId, LocalDate date) throws IllegalStateException { + // check employee + Optional employeeOpt = employeeRepository.findById(employeeId); + if (employeeOpt.isEmpty()) throw new IllegalStateException("Employee not found"); + + Optional placeOpt = placeRepository.findById(placeId); + if (placeOpt.isEmpty()) throw new IllegalStateException("Place not found"); + + // check if already booked + List exists = bookingRepository.findByPlaceAndDate(placeOpt.get(), date); + if (!exists.isEmpty()) { + throw new IllegalStateException("Already booked"); + } + + Booking booking = Booking.builder() + .date(date) + .employee(employeeOpt.get()) + .place(placeOpt.get()) + .build(); + return bookingRepository.save(booking); + } + + @Override + public List getBookingsForEmployee(long employeeId) { + return bookingRepository.findByEmployeeId(employeeId); + } } 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); + } }