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/controller/RootController.java b/src/main/java/com/example/nto/controller/RootController.java new file mode 100644 index 0000000..9412fd2 --- /dev/null +++ b/src/main/java/com/example/nto/controller/RootController.java @@ -0,0 +1,14 @@ +package com.example.nto.controller; + +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RestController; + +@RestController +public class RootController { + + @GetMapping("/") + public String home() { + return "Application is running"; + } +} + diff --git a/src/main/java/com/example/nto/entity/Booking.java b/src/main/java/com/example/nto/entity/Booking.java index 21c1981..5e87b63 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) + @JoinColumn(name = "place_id", nullable = false) private Place place; + @ManyToOne(fetch = FetchType.LAZY) + @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 void setId(Long id) { + this.id = id; + } + + public LocalDate getDate() { + return date; + } + + public void setDate(LocalDate date) { + this.date = date; + } + + public Place getPlace() { + return place; + } + + public void setPlace(Place place) { + this.place = place; + } + + public Employee getEmployee() { + return employee; + } + + 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..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..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..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..52c90ce 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.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..f26e6aa 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; -/** - * 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 + 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")); + + bookingRepository.findByPlaceAndDate(place, date) + .ifPresent(b -> { + throw new IllegalStateException("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); + } }