diff --git a/src/main/java/com/example/nto/App.java b/src/main/java/com/example/nto/App.java index e453f89..e5435c1 100644 --- a/src/main/java/com/example/nto/App.java +++ b/src/main/java/com/example/nto/App.java @@ -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); } } diff --git a/src/main/java/com/example/nto/controller/BookingController.java b/src/main/java/com/example/nto/controller/BookingController.java index 9885f84..3a24083 100644 --- a/src/main/java/com/example/nto/controller/BookingController.java +++ b/src/main/java/com/example/nto/controller/BookingController.java @@ -1,10 +1,79 @@ package com.example.nto.controller; +import com.example.nto.controller.dto.BookingRequest; +import com.example.nto.entity.Booking; +import com.example.nto.entity.Place; +import com.example.nto.service.BookingService; +import com.example.nto.service.EmployeeService; +import com.example.nto.service.PlaceService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; +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; + /** * TODO: ДОРАБОТАТЬ в рамках задания * ================================= * МОЖНО: Добавлять методы, аннотации, зависимости * НЕЛЬЗЯ: Изменять название класса и пакета */ +@RestController +@RequestMapping("/api/{code}") public class BookingController { + + @Autowired + private EmployeeService employeeService; + @Autowired + private BookingService bookingService; + @Autowired + private PlaceService placeService; + + @Value("${booking.days-ahead}") + private int daysAhead; + + @GetMapping("/booking") + public ResponseEntity>> getAvailablePlaces(@PathVariable String code) { + var user = employeeService.getByCode(code); + if (user == null) return ResponseEntity.status(HttpStatus.UNAUTHORIZED).build(); + try { + var today = LocalDate.now(); + var allPlaces = placeService.getAll(); + var upcomingBookings = bookingService.getAll(); + var bookedPlacesByDate = upcomingBookings.stream() + .collect(Collectors.groupingBy( + Booking::getDate, + Collectors.mapping(it -> it.getPlace().getId(), Collectors.toSet()) + )); + var result = new LinkedHashMap>(); + for (int i = 0; i <= daysAhead; i++) { + var date = today.plusDays(i); + var bookedPlaceIds = bookedPlacesByDate.getOrDefault(date, Collections.emptySet()); + var availablePlaces = allPlaces.stream() + .filter(it -> !bookedPlaceIds.contains(it.getId())) + .toList(); + result.put(date, availablePlaces); + } + return ResponseEntity.ok(result); + } catch (Exception e) { return ResponseEntity.status(HttpStatus.BAD_REQUEST).build(); } + } + + @PostMapping("/book") + public ResponseEntity book(@PathVariable String code, @RequestBody BookingRequest bookingRequest) { + var user = employeeService.getByCode(code); + if (user == null) return ResponseEntity.status(HttpStatus.UNAUTHORIZED).build(); + try { + var alreadyBooked = bookingService.getAll().stream() + .anyMatch(it -> it.getDate().isEqual(bookingRequest.getDate()) && it.getPlace().getId() == bookingRequest.getPlaceId()); + if (alreadyBooked) return ResponseEntity.status(HttpStatus.CONFLICT).build(); + var place = new Place(); + place.setId(bookingRequest.getPlaceId()); + bookingService.save(bookingRequest.getDate(), place, user); + return ResponseEntity.status(HttpStatus.CREATED).build(); + } catch (Exception e) { return ResponseEntity.status(HttpStatus.BAD_REQUEST).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..b6f3085 100644 --- a/src/main/java/com/example/nto/controller/EmployeeController.java +++ b/src/main/java/com/example/nto/controller/EmployeeController.java @@ -1,10 +1,53 @@ package com.example.nto.controller; +import com.example.nto.controller.dto.EmployeeInfoResponse; +import com.example.nto.entity.Booking; +import com.example.nto.service.EmployeeService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.*; + +import java.util.stream.Collectors; + /** * TODO: ДОРАБОТАТЬ в рамках задания * ================================= * МОЖНО: Добавлять методы, аннотации, зависимости * НЕЛЬЗЯ: Изменять название класса и пакета */ +@RestController +@RequestMapping("/api/{code}") public class EmployeeController { + + @Autowired + private EmployeeService employeeService; + + @GetMapping("/auth") + public ResponseEntity checkCode(@PathVariable String code) { + var user = employeeService.getByCode(code); + if (user == null) return ResponseEntity.status(HttpStatus.UNAUTHORIZED).build(); + return ResponseEntity.status(HttpStatus.OK).build(); + } + + @GetMapping("/info") + public ResponseEntity getInfo(@PathVariable String code) { + var user = employeeService.getByCode(code); + if (user == null) return ResponseEntity.status(HttpStatus.UNAUTHORIZED).build(); + var response = new EmployeeInfoResponse(); + response.setName(user.getName()); + response.setPhotoUrl(user.getPhotoUrl()); + var bookingMap = user.getBookingList().stream().collect(Collectors.toMap( + Booking::getDate, + it -> { + var p = new EmployeeInfoResponse.BookedPlace(); + p.setId(it.getPlace().getId()); + p.setPlace(it.getPlace().getPlace()); + return p; + }) + ); + response.setBooking(bookingMap); + + return ResponseEntity.ok(response); + } } diff --git a/src/main/java/com/example/nto/controller/dto/BookingRequest.java b/src/main/java/com/example/nto/controller/dto/BookingRequest.java new file mode 100644 index 0000000..1dcddd4 --- /dev/null +++ b/src/main/java/com/example/nto/controller/dto/BookingRequest.java @@ -0,0 +1,11 @@ +package com.example.nto.controller.dto; + +import lombok.Data; + +import java.time.LocalDate; + +@Data +public class BookingRequest { + private LocalDate date; + private long placeId; +} diff --git a/src/main/java/com/example/nto/controller/dto/EmployeeInfoResponse.java b/src/main/java/com/example/nto/controller/dto/EmployeeInfoResponse.java new file mode 100644 index 0000000..4e9c149 --- /dev/null +++ b/src/main/java/com/example/nto/controller/dto/EmployeeInfoResponse.java @@ -0,0 +1,19 @@ +package com.example.nto.controller.dto; + +import lombok.Data; + +import java.time.LocalDate; +import java.util.Map; + +@Data +public class EmployeeInfoResponse { + private String name; + private String photoUrl; + private Map booking; + + @Data + public static class BookedPlace { + private long id; + private String place; + } +} \ No newline at end of file diff --git a/src/main/java/com/example/nto/entity/Booking.java b/src/main/java/com/example/nto/entity/Booking.java index 21c1981..0a3afa5 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; @@ -19,10 +17,12 @@ import java.time.LocalDate; */ @Data @Builder -@NoArgsConstructor @AllArgsConstructor +@Entity public class Booking { + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) private long id; private LocalDate date; @@ -31,5 +31,10 @@ public class Booking { @JoinColumn(name = "place_id") private Place place; + @ManyToOne(targetEntity = Employee.class, fetch = FetchType.LAZY) + @JoinColumn(name = "employee_id") private Employee employee; + + public Booking() {} + } diff --git a/src/main/java/com/example/nto/entity/Employee.java b/src/main/java/com/example/nto/entity/Employee.java index a52102b..a534a93 100644 --- a/src/main/java/com/example/nto/entity/Employee.java +++ b/src/main/java/com/example/nto/entity/Employee.java @@ -17,10 +17,12 @@ import java.util.List; */ @Data @Builder -@NoArgsConstructor @AllArgsConstructor +@Entity public class Employee { + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) private long id; private String name; @@ -31,4 +33,7 @@ public class Employee { @OneToMany(mappedBy = "employee", cascade = CascadeType.ALL, fetch = FetchType.LAZY) private List bookingList; + + public Employee() {} + } diff --git a/src/main/java/com/example/nto/entity/Place.java b/src/main/java/com/example/nto/entity/Place.java index 00c253b..3a1343a 100644 --- a/src/main/java/com/example/nto/entity/Place.java +++ b/src/main/java/com/example/nto/entity/Place.java @@ -1,12 +1,11 @@ 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; -import lombok.NoArgsConstructor; + +import java.util.List; /** @@ -17,13 +16,20 @@ import lombok.NoArgsConstructor; */ @Data @Builder -@NoArgsConstructor @AllArgsConstructor +@Entity public class Place { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private long id; + @Column(name = "place_name") private String place; + + @OneToMany(mappedBy = "place", cascade = CascadeType.ALL, fetch = FetchType.LAZY) + private List bookingList; + + public 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..9b9ee31 100644 --- a/src/main/java/com/example/nto/repository/BookingRepository.java +++ b/src/main/java/com/example/nto/repository/BookingRepository.java @@ -1,10 +1,17 @@ package com.example.nto.repository; +import com.example.nto.entity.Booking; +import org.springframework.data.repository.CrudRepository; +import org.springframework.stereotype.Repository; + +import java.util.Optional; + /** * TODO: ДОРАБОТАТЬ в рамках задания * ================================= * МОЖНО: Добавлять методы, аннотации, зависимости * НЕЛЬЗЯ: Изменять название класса и пакета */ -public interface BookingRepository { -} +@Repository +public interface BookingRepository extends CrudRepository { +} \ No newline at end of file diff --git a/src/main/java/com/example/nto/repository/EmployeeRepository.java b/src/main/java/com/example/nto/repository/EmployeeRepository.java index 210d29c..d7eabee 100644 --- a/src/main/java/com/example/nto/repository/EmployeeRepository.java +++ b/src/main/java/com/example/nto/repository/EmployeeRepository.java @@ -1,10 +1,19 @@ package com.example.nto.repository; +import com.example.nto.entity.Employee; +import org.springframework.data.repository.CrudRepository; +import org.springframework.stereotype.Repository; + +import java.util.Optional; + /** * TODO: ДОРАБОТАТЬ в рамках задания * ================================= * МОЖНО: Добавлять методы, аннотации, зависимости * НЕЛЬЗЯ: Изменять название класса и пакета */ -public interface EmployeeRepository { +@Repository +public interface EmployeeRepository extends CrudRepository { + Optional findByName(String name); + 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..8b4b90b 100644 --- a/src/main/java/com/example/nto/repository/PlaceRepository.java +++ b/src/main/java/com/example/nto/repository/PlaceRepository.java @@ -1,10 +1,15 @@ package com.example.nto.repository; +import com.example.nto.entity.Place; +import org.springframework.data.repository.CrudRepository; +import org.springframework.stereotype.Repository; + /** * TODO: ДОРАБОТАТЬ в рамках задания * ================================= * МОЖНО: Добавлять методы, аннотации, зависимости * НЕЛЬЗЯ: Изменять название класса и пакета */ -public interface PlaceRepository { +@Repository +public interface PlaceRepository extends CrudRepository { } diff --git a/src/main/java/com/example/nto/service/BookingService.java b/src/main/java/com/example/nto/service/BookingService.java index 31ec148..f2ff5a3 100644 --- a/src/main/java/com/example/nto/service/BookingService.java +++ b/src/main/java/com/example/nto/service/BookingService.java @@ -1,5 +1,12 @@ package com.example.nto.service; +import com.example.nto.entity.Booking; +import com.example.nto.entity.Employee; +import com.example.nto.entity.Place; + +import java.time.LocalDate; +import java.util.List; + /** * TODO: ДОРАБОТАТЬ в рамках задания * ================================= @@ -7,4 +14,7 @@ package com.example.nto.service; * НЕЛЬЗЯ: Изменять название класса и пакета */ public interface BookingService { + Booking getById(Long id); + List getAll(); + void save(LocalDate date, Place place, Employee employee); } diff --git a/src/main/java/com/example/nto/service/EmployeeService.java b/src/main/java/com/example/nto/service/EmployeeService.java index cccd209..5b50ebe 100644 --- a/src/main/java/com/example/nto/service/EmployeeService.java +++ b/src/main/java/com/example/nto/service/EmployeeService.java @@ -1,5 +1,9 @@ package com.example.nto.service; +import com.example.nto.entity.Employee; + +import java.util.List; + /** * TODO: ДОРАБОТАТЬ в рамках задания * ================================= @@ -7,4 +11,8 @@ package com.example.nto.service; * НЕЛЬЗЯ: Изменять название класса и пакета */ public interface EmployeeService { + Employee getById(Long id); + Employee getByName(String name); + Employee getByCode(String code); + List getAll(); } diff --git a/src/main/java/com/example/nto/service/PlaceService.java b/src/main/java/com/example/nto/service/PlaceService.java new file mode 100644 index 0000000..2224d35 --- /dev/null +++ b/src/main/java/com/example/nto/service/PlaceService.java @@ -0,0 +1,10 @@ +package com.example.nto.service; + +import com.example.nto.entity.Place; + +import java.util.List; + +public interface PlaceService { + Place getById(Long id); + List getAll(); +} 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..3e58948 100644 --- a/src/main/java/com/example/nto/service/impl/BookingServiceImpl.java +++ b/src/main/java/com/example/nto/service/impl/BookingServiceImpl.java @@ -1,6 +1,18 @@ 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.service.BookingService; +import io.micrometer.common.lang.Nullable; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +import java.time.LocalDate; +import java.util.List; +import java.util.stream.Collectors; +import java.util.stream.StreamSupport; /** * TODO: ДОРАБОТАТЬ в рамках задания @@ -8,5 +20,29 @@ import com.example.nto.service.BookingService; * МОЖНО: Добавлять методы, аннотации, зависимости * НЕЛЬЗЯ: Изменять название класса и пакета */ +@Service public class BookingServiceImpl implements BookingService { + @Autowired + private BookingRepository bookingRepository; + + @Override + @Nullable + public Booking getById(Long id) { + return bookingRepository.findById(id).orElseGet(() -> null); + } + + @Override + public List getAll() { + return StreamSupport.stream(bookingRepository.findAll().spliterator(), false).collect(Collectors.toList()); + } + + @Override + public void save(LocalDate date, Place place, Employee employee) { + var booking = Booking.builder() + .date(date) + .place(place) + .employee(employee) + .build(); + 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..6a969f7 100644 --- a/src/main/java/com/example/nto/service/impl/EmployeeServiceImpl.java +++ b/src/main/java/com/example/nto/service/impl/EmployeeServiceImpl.java @@ -1,6 +1,16 @@ package com.example.nto.service.impl; +import com.example.nto.entity.Booking; +import com.example.nto.entity.Employee; +import com.example.nto.repository.EmployeeRepository; import com.example.nto.service.EmployeeService; +import io.micrometer.common.lang.Nullable; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +import java.util.List; +import java.util.stream.Collectors; +import java.util.stream.StreamSupport; /** * TODO: ДОРАБОТАТЬ в рамках задания @@ -8,5 +18,31 @@ import com.example.nto.service.EmployeeService; * МОЖНО: Добавлять методы, аннотации, зависимости * НЕЛЬЗЯ: Изменять название класса и пакета */ +@Service public class EmployeeServiceImpl implements EmployeeService { + @Autowired + private EmployeeRepository employeeRepository; + + @Override + @Nullable + public Employee getById(Long id) { + return employeeRepository.findById(id).orElseGet(() -> null); + } + + @Override + @Nullable + public Employee getByName(String name) { + return employeeRepository.findByName(name).orElseGet(() -> null); + } + + @Override + @Nullable + public Employee getByCode(String code) { + return employeeRepository.findByCode(code).orElseGet(() -> null); + } + + @Override + public List getAll() { + return StreamSupport.stream(employeeRepository.findAll().spliterator(), false).collect(Collectors.toList()); + } } diff --git a/src/main/java/com/example/nto/service/impl/PlaceServiceImpl.java b/src/main/java/com/example/nto/service/impl/PlaceServiceImpl.java new file mode 100644 index 0000000..55d1ecf --- /dev/null +++ b/src/main/java/com/example/nto/service/impl/PlaceServiceImpl.java @@ -0,0 +1,29 @@ +package com.example.nto.service.impl; + +import com.example.nto.entity.Place; +import com.example.nto.repository.PlaceRepository; +import com.example.nto.service.PlaceService; +import io.micrometer.common.lang.Nullable; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +import java.util.List; +import java.util.stream.Collectors; +import java.util.stream.StreamSupport; + +@Service +public class PlaceServiceImpl implements PlaceService { + @Autowired + private PlaceRepository placeRepository; + + @Override + @Nullable + public Place getById(Long id) { + return placeRepository.findById(id).orElseGet(() -> null); + } + + @Override + public List getAll() { + return StreamSupport.stream(placeRepository.findAll().spliterator(), false).collect(Collectors.toList()); + } +}