From a6954c20134f6d257945700d19dce7bee199e033 Mon Sep 17 00:00:00 2001 From: ci-bot Date: Mon, 24 Nov 2025 17:17:06 +0000 Subject: [PATCH 1/5] Update .gitea/workflows/workflow.yml --- .gitea/workflows/workflow.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.gitea/workflows/workflow.yml b/.gitea/workflows/workflow.yml index 20fe015..5690186 100644 --- a/.gitea/workflows/workflow.yml +++ b/.gitea/workflows/workflow.yml @@ -23,7 +23,7 @@ jobs: GITEA_HEAD_REF: ${{ gitea.event.pull_request.head.ref }} - name: Checkout tests - run: python3 /opt/scripts/copy-tests.py --repo-url "Olympic/NTO-2025-Android-TeamTask-tests" --branch "main" --task-type "spring" + run: python3 /opt/scripts/copy-tests.py --repo-url "Olympic/NTO-2025-Backend-TeamTask-tests" --branch "main" --task-type "spring" - name: Run tests run: mvn test -- 2.34.1 From d5564b63eb9319d0865abeb79d6170099cf865df Mon Sep 17 00:00:00 2001 From: gordei Date: Sat, 29 Nov 2025 15:31:17 +0300 Subject: [PATCH 2/5] =?UTF-8?q?=D0=97=D0=B0=D0=B4=D0=B0=D0=BD=D0=B8=D1=8F?= =?UTF-8?q?=20Backend=20=D0=BD=D1=82=D0=BE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/com/example/nto/App.java | 11 +- .../nto/controller/BookingController.java | 135 +++++++++++++++++- .../nto/controller/EmployeeController.java | 29 +++- .../java/com/example/nto/entity/Booking.java | 48 ++++--- .../java/com/example/nto/entity/Employee.java | 47 +++--- .../java/com/example/nto/entity/Place.java | 55 ++++--- .../nto/repository/BookingRepository.java | 20 ++- .../nto/repository/EmployeeRepository.java | 13 +- .../nto/repository/PlaceRepository.java | 11 +- .../example/nto/service/BookingService.java | 18 ++- .../example/nto/service/EmployeeService.java | 10 +- .../com/example/nto/service/PlaceService.java | 10 ++ .../nto/service/impl/BookingServiceImpl.java | 46 +++++- .../nto/service/impl/EmployeeServiceImpl.java | 22 ++- .../nto/service/impl/PlaceServiceImpl.java | 29 ++++ 15 files changed, 385 insertions(+), 119 deletions(-) create mode 100644 src/main/java/com/example/nto/service/PlaceService.java create mode 100644 src/main/java/com/example/nto/service/impl/PlaceServiceImpl.java 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..579606f 100644 --- a/src/main/java/com/example/nto/controller/BookingController.java +++ b/src/main/java/com/example/nto/controller/BookingController.java @@ -1,10 +1,133 @@ package com.example.nto.controller; -/** - * TODO: ДОРАБОТАТЬ в рамках задания - * ================================= - * МОЖНО: Добавлять методы, аннотации, зависимости - * НЕЛЬЗЯ: Изменять название класса и пакета - */ +import com.example.nto.entity.Booking; +import com.example.nto.entity.Employee; +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.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 +@RequestMapping("/api/{code}") public class BookingController { + + private final EmployeeService employeeService; + private final BookingService bookingService; + private final PlaceService placeService; + + public BookingController(EmployeeService employeeService, BookingService bookingService, PlaceService placeService) { + this.employeeService = employeeService; + this.bookingService = bookingService; + this.placeService = placeService; + } + + @GetMapping("/auth") + public ResponseEntity auth(@PathVariable String code) { + Optional employeeOpt = employeeService.findByCode(code); + if (employeeOpt.isPresent()) { + return ResponseEntity.ok().build(); + } else { + return ResponseEntity.status(HttpStatus.UNAUTHORIZED).build(); + } + } + + + + + @GetMapping("/info") + public ResponseEntity> info(@PathVariable String code) { + Optional employeeOpt = employeeService.findByCode(code); + if (employeeOpt.isEmpty()) return ResponseEntity.status(HttpStatus.UNAUTHORIZED).build(); + + Employee employee = employeeOpt.get(); + + Map response = new HashMap<>(); + response.put("name", employee.getName()); + response.put("photoUrl", employee.getPhotoUrl()); + + Map>> bookingsMap = new HashMap<>(); + List bookingList = bookingService.getBookingsByEmployee(employee); + + for (Booking b : bookingList) { + String dateKey = b.getDate().toString(); + Map bookingData = new HashMap<>(); + bookingData.put("id", b.getId()); + bookingData.put("place", b.getPlace().getPlace()); + + bookingsMap.computeIfAbsent(dateKey, k -> new ArrayList<>()).add(bookingData); + } + + response.put("booking", bookingsMap); + + return ResponseEntity.ok(response); + } + + @GetMapping("/booking") + public ResponseEntity>>> availableBooking(@PathVariable String code) { + Optional employeeOpt = employeeService.findByCode(code); + if (employeeOpt.isEmpty()) return ResponseEntity.status(HttpStatus.UNAUTHORIZED).build(); + + Map>> result = new LinkedHashMap<>(); + LocalDate today = LocalDate.now(); + List allPlaces = placeService.getAllPlaces(); + + for (int i = 0; i < 4; i++) { + LocalDate date = today.plusDays(i); + List bookings = bookingService.getBookingsByDate(date); + + Set bookedPlaceIds = bookings.stream() + .map(b -> b.getPlace().getId()) + .collect(Collectors.toSet()); + + List> available = allPlaces.stream() + .filter(p -> !bookedPlaceIds.contains(p.getId())) + .map(p -> { + Map m = new HashMap<>(); + m.put("id", p.getId()); + m.put("place", p.getPlace()); + return m; + }) + .collect(Collectors.toList()); + + result.put(date.toString(), available); + } + + return ResponseEntity.ok(result); + } + + + @PostMapping("/book") + public ResponseEntity book(@PathVariable String code, @RequestBody Map body) { + Optional employeeOpt = employeeService.findByCode(code); + if (employeeOpt.isEmpty()) return ResponseEntity.status(HttpStatus.UNAUTHORIZED).build(); + Employee employee = employeeOpt.get(); + + try { + String dateStr = body.get("date"); + String placeIdStr = body.get("placeID"); + if (dateStr == null || placeIdStr == null) { + return ResponseEntity.status(HttpStatus.BAD_REQUEST).build(); + } + + LocalDate date = LocalDate.parse(dateStr); + Long placeId = Long.parseLong(placeIdStr); + Place place = placeService.getPlaceById(placeId); + + if (bookingService.getBookingByDateAndPlace(date, place).isPresent()) { + return ResponseEntity.status(HttpStatus.CONFLICT).build(); + } + + bookingService.createBooking(employee, place, date); + 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..4763ec3 100644 --- a/src/main/java/com/example/nto/controller/EmployeeController.java +++ b/src/main/java/com/example/nto/controller/EmployeeController.java @@ -1,10 +1,27 @@ package com.example.nto.controller; -/** - * TODO: ДОРАБОТАТЬ в рамках задания - * ================================= - * МОЖНО: Добавлять методы, аннотации, зависимости - * НЕЛЬЗЯ: Изменять название класса и пакета - */ +import com.example.nto.entity.Employee; +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.Optional; + +@RestController +@RequestMapping("/api/employee") public class EmployeeController { + + private final EmployeeService employeeService; + + public EmployeeController(EmployeeService employeeService) { + this.employeeService = employeeService; + } + + @GetMapping("/{code}") + public ResponseEntity getEmployeeByCode(@PathVariable String code) { + Optional employeeOpt = employeeService.findByCode(code); + return employeeOpt.map(ResponseEntity::ok) + .orElseGet(() -> ResponseEntity.status(HttpStatus.NOT_FOUND).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..28f33f3 100644 --- a/src/main/java/com/example/nto/entity/Booking.java +++ b/src/main/java/com/example/nto/entity/Booking.java @@ -1,35 +1,43 @@ 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 { + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) private long id; private LocalDate date; - @ManyToOne(targetEntity = Place.class, fetch = FetchType.LAZY) + @ManyToOne(fetch = FetchType.LAZY) @JoinColumn(name = "place_id") private Place place; + @ManyToOne(fetch = FetchType.LAZY) + @JoinColumn(name = "employee_id") private Employee employee; + + public Booking() {} + + public Booking(Employee employee, Place place, LocalDate date) { + this.employee = employee; + this.place = place; + this.date = date; + } + + 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..4f1034f 100644 --- a/src/main/java/com/example/nto/entity/Employee.java +++ b/src/main/java/com/example/nto/entity/Employee.java @@ -1,34 +1,47 @@ package com.example.nto.entity; import jakarta.persistence.*; -import lombok.AllArgsConstructor; -import lombok.Builder; -import lombok.Data; -import lombok.NoArgsConstructor; - +import java.util.ArrayList; import java.util.List; - -/** - * TODO: ДОРАБОТАТЬ в рамках задания - * ================================= - * МОЖНО: Добавлять методы, аннотации, зависимости - * НЕЛЬЗЯ: Изменять название класса и пакета - */ -@Data -@Builder -@NoArgsConstructor -@AllArgsConstructor +@Entity +@Table(name = "employee") public class Employee { + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) private long id; private String name; + @Column(unique = true) private String code; private String photoUrl; @OneToMany(mappedBy = "employee", cascade = CascadeType.ALL, fetch = FetchType.LAZY) - private List bookingList; + private List bookingList = new ArrayList<>(); + + public Employee() {} + + public Employee(String name, String code, String photoUrl) { + this.name = name; + this.code = code; + this.photoUrl = photoUrl; + } + + public long getId() { return id; } + public void setId(long id) { this.id = id; } + + public String getName() { return name; } + public void setName(String name) { this.name = name; } + + public String getCode() { return code; } + public void setCode(String code) { this.code = code; } + + public String getPhotoUrl() { return photoUrl; } + public void setPhotoUrl(String photoUrl) { this.photoUrl = photoUrl; } + + public List getBookingList() { return bookingList; } + 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..d88b10b 100644 --- a/src/main/java/com/example/nto/entity/Place.java +++ b/src/main/java/com/example/nto/entity/Place.java @@ -1,29 +1,46 @@ 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, unique = true) private String place; + + public Place() { + } + + public Place(String place) { + this.place = place; + } + + public Long getId() { + return id; + } + + public void setId(Long id) { + this.id = id; + } + + public String getPlace() { + return place; + } + + public void setPlace(String place) { + this.place = place; + } + + @Override + public String toString() { + return "Place{" + + "id=" + id + + ", 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..54291a7 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.Employee; +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 findByEmployee(Employee employee); + List findByDate(LocalDate date); + Optional findByDateAndPlace(LocalDate date, Place place); } diff --git a/src/main/java/com/example/nto/repository/EmployeeRepository.java b/src/main/java/com/example/nto/repository/EmployeeRepository.java index 210d29c..1a4f77a 100644 --- a/src/main/java/com/example/nto/repository/EmployeeRepository.java +++ b/src/main/java/com/example/nto/repository/EmployeeRepository.java @@ -1,10 +1,9 @@ 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..51f5f60 100644 --- a/src/main/java/com/example/nto/service/BookingService.java +++ b/src/main/java/com/example/nto/service/BookingService.java @@ -1,10 +1,16 @@ package com.example.nto.service; -/** - * TODO: ДОРАБОТАТЬ в рамках задания - * ================================= - * МОЖНО: Добавлять методы, аннотации, зависимости - * НЕЛЬЗЯ: Изменять название класса и пакета - */ +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; +import java.util.Optional; + public interface BookingService { + List getBookingsByEmployee(Employee employee); + List getBookingsByDate(LocalDate date); + Optional getBookingByDateAndPlace(LocalDate date, Place place); + Booking createBooking(Employee employee, Place place, LocalDate date); } diff --git a/src/main/java/com/example/nto/service/EmployeeService.java b/src/main/java/com/example/nto/service/EmployeeService.java index cccd209..a300e34 100644 --- a/src/main/java/com/example/nto/service/EmployeeService.java +++ b/src/main/java/com/example/nto/service/EmployeeService.java @@ -1,10 +1,8 @@ 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/PlaceService.java b/src/main/java/com/example/nto/service/PlaceService.java new file mode 100644 index 0000000..37e28ac --- /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 { + List getAllPlaces(); + Place getPlaceById(Long id); +} 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..51b0d19 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,46 @@ 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 org.springframework.stereotype.Service; -/** - * TODO: ДОРАБОТАТЬ в рамках задания - * ================================= - * МОЖНО: Добавлять методы, аннотации, зависимости - * НЕЛЬЗЯ: Изменять название класса и пакета - */ +import java.time.LocalDate; +import java.util.List; +import java.util.Optional; + +@Service public class BookingServiceImpl implements BookingService { + + private final BookingRepository bookingRepository; + + public BookingServiceImpl(BookingRepository bookingRepository) { + this.bookingRepository = bookingRepository; + } + + @Override + public List getBookingsByEmployee(Employee employee) { + return bookingRepository.findByEmployee(employee); + } + + @Override + public List getBookingsByDate(LocalDate date) { + return bookingRepository.findByDate(date); + } + + @Override + public Optional getBookingByDateAndPlace(LocalDate date, Place place) { + return bookingRepository.findByDateAndPlace(date, place); + } + + @Override + public Booking createBooking(Employee employee, Place place, LocalDate date) { + Booking booking = new Booking(); + booking.setEmployee(employee); + booking.setPlace(place); + booking.setDate(date); + return 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..6939037 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,22 @@ 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; +import java.util.Optional; -/** - * TODO: ДОРАБОТАТЬ в рамках задания - * ================================= - * МОЖНО: Добавлять методы, аннотации, зависимости - * НЕЛЬЗЯ: Изменять название класса и пакета - */ +@Service public class EmployeeServiceImpl implements EmployeeService { + + private final EmployeeRepository employeeRepository; + + public EmployeeServiceImpl(EmployeeRepository employeeRepository) { + this.employeeRepository = employeeRepository; + } + + @Override + public Optional findByCode(String code) { + return employeeRepository.findByCode(code); + } } 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..3ab1cb1 --- /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 org.springframework.stereotype.Service; + +import java.util.List; + +@Service +public class PlaceServiceImpl implements PlaceService { + + private final PlaceRepository placeRepository; + + public PlaceServiceImpl(PlaceRepository placeRepository) { + this.placeRepository = placeRepository; + } + + @Override + public List getAllPlaces() { + return placeRepository.findAll(); + } + + @Override + public Place getPlaceById(Long id) { + return placeRepository.findById(id) + .orElseThrow(() -> new RuntimeException("Place not found")); + } +} -- 2.34.1 From 0fc06783920f96d7f475aee0a25aff2e6c5c68ad Mon Sep 17 00:00:00 2001 From: gordei Date: Sat, 29 Nov 2025 19:38:16 +0300 Subject: [PATCH 3/5] =?UTF-8?q?=D0=B2=D1=82=D0=BE=D1=80=D0=B0=D1=8F=20?= =?UTF-8?q?=D0=BF=D0=BE=D0=BF=D1=8B=D1=82=D0=BA=D0=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../nto/controller/BookingController.java | 20 ++++++++++++------- .../nto/repository/BookingRepository.java | 2 ++ .../example/nto/service/BookingService.java | 3 +++ .../nto/service/impl/BookingServiceImpl.java | 5 +++++ 4 files changed, 23 insertions(+), 7 deletions(-) diff --git a/src/main/java/com/example/nto/controller/BookingController.java b/src/main/java/com/example/nto/controller/BookingController.java index 579606f..70c1548 100644 --- a/src/main/java/com/example/nto/controller/BookingController.java +++ b/src/main/java/com/example/nto/controller/BookingController.java @@ -38,9 +38,6 @@ public class BookingController { } } - - - @GetMapping("/info") public ResponseEntity> info(@PathVariable String code) { Optional employeeOpt = employeeService.findByCode(code); @@ -52,7 +49,7 @@ public class BookingController { response.put("name", employee.getName()); response.put("photoUrl", employee.getPhotoUrl()); - Map>> bookingsMap = new HashMap<>(); + Map> bookingsMap = new HashMap<>(); List bookingList = bookingService.getBookingsByEmployee(employee); for (Booking b : bookingList) { @@ -61,7 +58,7 @@ public class BookingController { bookingData.put("id", b.getId()); bookingData.put("place", b.getPlace().getPlace()); - bookingsMap.computeIfAbsent(dateKey, k -> new ArrayList<>()).add(bookingData); + bookingsMap.put(dateKey, bookingData); } response.put("booking", bookingsMap); @@ -102,7 +99,6 @@ public class BookingController { return ResponseEntity.ok(result); } - @PostMapping("/book") public ResponseEntity book(@PathVariable String code, @RequestBody Map body) { Optional employeeOpt = employeeService.findByCode(code); @@ -111,7 +107,12 @@ public class BookingController { try { String dateStr = body.get("date"); - String placeIdStr = body.get("placeID"); + + // поддерживаем все варианты ключей + String placeIdStr = body.getOrDefault("placeID", + body.getOrDefault("placeId", + body.getOrDefault("place", null))); + if (dateStr == null || placeIdStr == null) { return ResponseEntity.status(HttpStatus.BAD_REQUEST).build(); } @@ -120,12 +121,17 @@ public class BookingController { Long placeId = Long.parseLong(placeIdStr); Place place = placeService.getPlaceById(placeId); + if (bookingService.getBookingByEmployeeAndDate(employee, date).isPresent()) { + return ResponseEntity.status(HttpStatus.CONFLICT).build(); + } + if (bookingService.getBookingByDateAndPlace(date, place).isPresent()) { return ResponseEntity.status(HttpStatus.CONFLICT).build(); } bookingService.createBooking(employee, place, date); 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/repository/BookingRepository.java b/src/main/java/com/example/nto/repository/BookingRepository.java index 54291a7..c6fc3eb 100644 --- a/src/main/java/com/example/nto/repository/BookingRepository.java +++ b/src/main/java/com/example/nto/repository/BookingRepository.java @@ -13,4 +13,6 @@ public interface BookingRepository extends JpaRepository { List findByEmployee(Employee employee); List findByDate(LocalDate date); Optional findByDateAndPlace(LocalDate date, Place place); + + Optional findByEmployeeAndDate(Employee employee, LocalDate date); } diff --git a/src/main/java/com/example/nto/service/BookingService.java b/src/main/java/com/example/nto/service/BookingService.java index 51f5f60..fe191ea 100644 --- a/src/main/java/com/example/nto/service/BookingService.java +++ b/src/main/java/com/example/nto/service/BookingService.java @@ -12,5 +12,8 @@ public interface BookingService { List getBookingsByEmployee(Employee employee); List getBookingsByDate(LocalDate date); Optional getBookingByDateAndPlace(LocalDate date, Place place); + + Optional getBookingByEmployeeAndDate(Employee employee, LocalDate date); + Booking createBooking(Employee employee, Place place, LocalDate date); } 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 51b0d19..2091f53 100644 --- a/src/main/java/com/example/nto/service/impl/BookingServiceImpl.java +++ b/src/main/java/com/example/nto/service/impl/BookingServiceImpl.java @@ -35,6 +35,11 @@ public class BookingServiceImpl implements BookingService { return bookingRepository.findByDateAndPlace(date, place); } + @Override + public Optional getBookingByEmployeeAndDate(Employee employee, LocalDate date) { + return bookingRepository.findByEmployeeAndDate(employee, date); + } + @Override public Booking createBooking(Employee employee, Place place, LocalDate date) { Booking booking = new Booking(); -- 2.34.1 From c4afce7fa6798acbf6bc60757657f82ef6ffcbd3 Mon Sep 17 00:00:00 2001 From: gordei Date: Sat, 29 Nov 2025 20:11:44 +0300 Subject: [PATCH 4/5] =?UTF-8?q?=D1=82=D1=80=D0=B5=D1=82=D1=8C=D1=8F=20?= =?UTF-8?q?=D0=BF=D0=BE=D0=BF=D1=8B=D1=82=D0=BA=D0=B0((((?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../nto/controller/BookingController.java | 9 ++--- .../nto/controller/EmployeeController.java | 7 ++-- .../java/com/example/nto/entity/Booking.java | 9 +++-- .../java/com/example/nto/entity/Employee.java | 6 ++-- .../java/com/example/nto/entity/Place.java | 35 ++++--------------- 5 files changed, 19 insertions(+), 47 deletions(-) diff --git a/src/main/java/com/example/nto/controller/BookingController.java b/src/main/java/com/example/nto/controller/BookingController.java index 70c1548..f4b8c0a 100644 --- a/src/main/java/com/example/nto/controller/BookingController.java +++ b/src/main/java/com/example/nto/controller/BookingController.java @@ -37,19 +37,17 @@ public class BookingController { return ResponseEntity.status(HttpStatus.UNAUTHORIZED).build(); } } - @GetMapping("/info") public ResponseEntity> info(@PathVariable String code) { Optional employeeOpt = employeeService.findByCode(code); if (employeeOpt.isEmpty()) return ResponseEntity.status(HttpStatus.UNAUTHORIZED).build(); Employee employee = employeeOpt.get(); - Map response = new HashMap<>(); response.put("name", employee.getName()); response.put("photoUrl", employee.getPhotoUrl()); - Map> bookingsMap = new HashMap<>(); + Map>> bookingsMap = new HashMap<>(); List bookingList = bookingService.getBookingsByEmployee(employee); for (Booking b : bookingList) { @@ -58,11 +56,10 @@ public class BookingController { bookingData.put("id", b.getId()); bookingData.put("place", b.getPlace().getPlace()); - bookingsMap.put(dateKey, bookingData); + bookingsMap.computeIfAbsent(dateKey, k -> new ArrayList<>()).add(bookingData); } response.put("booking", bookingsMap); - return ResponseEntity.ok(response); } @@ -107,8 +104,6 @@ public class BookingController { try { String dateStr = body.get("date"); - - // поддерживаем все варианты ключей String placeIdStr = body.getOrDefault("placeID", body.getOrDefault("placeId", body.getOrDefault("place", null))); diff --git a/src/main/java/com/example/nto/controller/EmployeeController.java b/src/main/java/com/example/nto/controller/EmployeeController.java index 4763ec3..527340c 100644 --- a/src/main/java/com/example/nto/controller/EmployeeController.java +++ b/src/main/java/com/example/nto/controller/EmployeeController.java @@ -21,7 +21,10 @@ public class EmployeeController { @GetMapping("/{code}") public ResponseEntity getEmployeeByCode(@PathVariable String code) { Optional employeeOpt = employeeService.findByCode(code); - return employeeOpt.map(ResponseEntity::ok) - .orElseGet(() -> ResponseEntity.status(HttpStatus.NOT_FOUND).build()); + if (employeeOpt.isPresent()) { + return ResponseEntity.ok(employeeOpt.get()); + } else { + return ResponseEntity.status(HttpStatus.NOT_FOUND).build(); + } } } diff --git a/src/main/java/com/example/nto/entity/Booking.java b/src/main/java/com/example/nto/entity/Booking.java index 28f33f3..d4dd146 100644 --- a/src/main/java/com/example/nto/entity/Booking.java +++ b/src/main/java/com/example/nto/entity/Booking.java @@ -1,10 +1,12 @@ package com.example.nto.entity; +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; import jakarta.persistence.*; import java.time.LocalDate; @Entity @Table(name = "booking") +@JsonIgnoreProperties({"hibernateLazyInitializer"}) public class Booking { @Id @@ -13,11 +15,11 @@ public class Booking { private LocalDate date; - @ManyToOne(fetch = FetchType.LAZY) + @ManyToOne(fetch = FetchType.EAGER) @JoinColumn(name = "place_id") private Place place; - @ManyToOne(fetch = FetchType.LAZY) + @ManyToOne(fetch = FetchType.EAGER) @JoinColumn(name = "employee_id") private Employee employee; @@ -31,13 +33,10 @@ public class Booking { 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 4f1034f..b1a34ef 100644 --- a/src/main/java/com/example/nto/entity/Employee.java +++ b/src/main/java/com/example/nto/entity/Employee.java @@ -1,5 +1,6 @@ package com.example.nto.entity; +import com.fasterxml.jackson.annotation.JsonIgnore; import jakarta.persistence.*; import java.util.ArrayList; import java.util.List; @@ -20,6 +21,7 @@ public class Employee { private String photoUrl; @OneToMany(mappedBy = "employee", cascade = CascadeType.ALL, fetch = FetchType.LAZY) + @JsonIgnore private List bookingList = new ArrayList<>(); public Employee() {} @@ -32,16 +34,12 @@ public class Employee { public long getId() { return id; } public void setId(long id) { this.id = id; } - public String getName() { return name; } public void setName(String name) { this.name = name; } - public String getCode() { return code; } public void setCode(String code) { this.code = code; } - public String getPhotoUrl() { return photoUrl; } public void setPhotoUrl(String photoUrl) { this.photoUrl = photoUrl; } - public List getBookingList() { return bookingList; } 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 d88b10b..0981a3a 100644 --- a/src/main/java/com/example/nto/entity/Place.java +++ b/src/main/java/com/example/nto/entity/Place.java @@ -13,34 +13,11 @@ public class Place { @Column(name = "place_name", nullable = false, unique = true) private String place; - public Place() { - } + public Place() {} + public Place(String place) { this.place = place; } - public Place(String place) { - this.place = place; - } - - public Long getId() { - return id; - } - - public void setId(Long id) { - this.id = id; - } - - public String getPlace() { - return place; - } - - public void setPlace(String place) { - this.place = place; - } - - @Override - public String toString() { - return "Place{" + - "id=" + id + - ", place='" + place + '\'' + - '}'; - } + public Long getId() { return id; } + public void setId(Long id) { this.id = id; } + public String getPlace() { return place; } + public void setPlace(String place) { this.place = place; } } -- 2.34.1 From 8f8aa1b8bb882f03840bf72b346afc13b7da18fb Mon Sep 17 00:00:00 2001 From: rrrrde1go Date: Sat, 21 Feb 2026 10:06:39 +0300 Subject: [PATCH 5/5] =?UTF-8?q?=D1=80=D0=B5=D1=88=D0=B5=D0=BD=D0=B8=D0=B5?= =?UTF-8?q?=202=20=D1=8D=D1=82=D0=B0=D0=BF=D0=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../nto/controller/BookingController.java | 134 +++--------------- .../nto/controller/EmployeeController.java | 27 ++-- .../nto/controller/dto/BookingCreateDto.java | 18 +++ .../nto/controller/dto/EmployeeDto.java | 32 +++++ .../example/nto/controller/dto/PlaceDto.java | 18 +++ .../java/com/example/nto/entity/Booking.java | 31 ++-- .../java/com/example/nto/entity/Employee.java | 36 ++--- .../java/com/example/nto/entity/Place.java | 20 +-- .../BookingAlreadyExistException.java | 7 + .../exception/EmployeeNotFoundException.java | 7 + .../nto/exception/PlaceNotFoundException.java | 7 + .../handler/GlobalExceptionHandler.java | 35 +++++ .../nto/repository/BookingRepository.java | 6 +- .../nto/repository/EmployeeRepository.java | 3 + .../example/nto/service/BookingService.java | 14 +- .../example/nto/service/EmployeeService.java | 8 +- .../com/example/nto/service/PlaceService.java | 10 -- .../nto/service/impl/BookingServiceImpl.java | 101 +++++++++---- .../nto/service/impl/EmployeeServiceImpl.java | 25 ++-- .../nto/service/impl/PlaceServiceImpl.java | 29 ---- 20 files changed, 295 insertions(+), 273 deletions(-) create mode 100644 src/main/java/com/example/nto/controller/dto/BookingCreateDto.java create mode 100644 src/main/java/com/example/nto/controller/dto/EmployeeDto.java create mode 100644 src/main/java/com/example/nto/controller/dto/PlaceDto.java create mode 100644 src/main/java/com/example/nto/exception/BookingAlreadyExistException.java create mode 100644 src/main/java/com/example/nto/exception/EmployeeNotFoundException.java create mode 100644 src/main/java/com/example/nto/exception/PlaceNotFoundException.java create mode 100644 src/main/java/com/example/nto/exception/handler/GlobalExceptionHandler.java delete mode 100644 src/main/java/com/example/nto/service/PlaceService.java delete mode 100644 src/main/java/com/example/nto/service/impl/PlaceServiceImpl.java diff --git a/src/main/java/com/example/nto/controller/BookingController.java b/src/main/java/com/example/nto/controller/BookingController.java index f4b8c0a..80f885e 100644 --- a/src/main/java/com/example/nto/controller/BookingController.java +++ b/src/main/java/com/example/nto/controller/BookingController.java @@ -1,134 +1,32 @@ package com.example.nto.controller; -import com.example.nto.entity.Booking; -import com.example.nto.entity.Employee; -import com.example.nto.entity.Place; +import com.example.nto.controller.dto.BookingCreateDto; +import com.example.nto.controller.dto.PlaceDto; import com.example.nto.service.BookingService; -import com.example.nto.service.EmployeeService; -import com.example.nto.service.PlaceService; +import lombok.RequiredArgsConstructor; import org.springframework.http.HttpStatus; -import org.springframework.http.ResponseEntity; +import org.springframework.validation.annotation.Validated; import org.springframework.web.bind.annotation.*; import java.time.LocalDate; import java.util.*; -import java.util.stream.Collectors; - +@Validated @RestController -@RequestMapping("/api/{code}") +@RequestMapping("api") +@RequiredArgsConstructor public class BookingController { - private final EmployeeService employeeService; - private final BookingService bookingService; - private final PlaceService placeService; + private static BookingService bookingService; - public BookingController(EmployeeService employeeService, BookingService bookingService, PlaceService placeService) { - this.employeeService = employeeService; - this.bookingService = bookingService; - this.placeService = placeService; + @GetMapping("/{code}/booking") + @ResponseStatus(code = HttpStatus.OK) + public Map> getByDate(@PathVariable String code) { + return bookingService.getFreePLace(code); } - @GetMapping("/auth") - public ResponseEntity auth(@PathVariable String code) { - Optional employeeOpt = employeeService.findByCode(code); - if (employeeOpt.isPresent()) { - return ResponseEntity.ok().build(); - } else { - return ResponseEntity.status(HttpStatus.UNAUTHORIZED).build(); - } - } - @GetMapping("/info") - public ResponseEntity> info(@PathVariable String code) { - Optional employeeOpt = employeeService.findByCode(code); - if (employeeOpt.isEmpty()) return ResponseEntity.status(HttpStatus.UNAUTHORIZED).build(); - - Employee employee = employeeOpt.get(); - Map response = new HashMap<>(); - response.put("name", employee.getName()); - response.put("photoUrl", employee.getPhotoUrl()); - - Map>> bookingsMap = new HashMap<>(); - List bookingList = bookingService.getBookingsByEmployee(employee); - - for (Booking b : bookingList) { - String dateKey = b.getDate().toString(); - Map bookingData = new HashMap<>(); - bookingData.put("id", b.getId()); - bookingData.put("place", b.getPlace().getPlace()); - - bookingsMap.computeIfAbsent(dateKey, k -> new ArrayList<>()).add(bookingData); - } - - response.put("booking", bookingsMap); - return ResponseEntity.ok(response); - } - - @GetMapping("/booking") - public ResponseEntity>>> availableBooking(@PathVariable String code) { - Optional employeeOpt = employeeService.findByCode(code); - if (employeeOpt.isEmpty()) return ResponseEntity.status(HttpStatus.UNAUTHORIZED).build(); - - Map>> result = new LinkedHashMap<>(); - LocalDate today = LocalDate.now(); - List allPlaces = placeService.getAllPlaces(); - - for (int i = 0; i < 4; i++) { - LocalDate date = today.plusDays(i); - List bookings = bookingService.getBookingsByDate(date); - - Set bookedPlaceIds = bookings.stream() - .map(b -> b.getPlace().getId()) - .collect(Collectors.toSet()); - - List> available = allPlaces.stream() - .filter(p -> !bookedPlaceIds.contains(p.getId())) - .map(p -> { - Map m = new HashMap<>(); - m.put("id", p.getId()); - m.put("place", p.getPlace()); - return m; - }) - .collect(Collectors.toList()); - - result.put(date.toString(), available); - } - - return ResponseEntity.ok(result); - } - - @PostMapping("/book") - public ResponseEntity book(@PathVariable String code, @RequestBody Map body) { - Optional employeeOpt = employeeService.findByCode(code); - if (employeeOpt.isEmpty()) return ResponseEntity.status(HttpStatus.UNAUTHORIZED).build(); - Employee employee = employeeOpt.get(); - - try { - String dateStr = body.get("date"); - String placeIdStr = body.getOrDefault("placeID", - body.getOrDefault("placeId", - body.getOrDefault("place", null))); - - if (dateStr == null || placeIdStr == null) { - return ResponseEntity.status(HttpStatus.BAD_REQUEST).build(); - } - - LocalDate date = LocalDate.parse(dateStr); - Long placeId = Long.parseLong(placeIdStr); - Place place = placeService.getPlaceById(placeId); - - if (bookingService.getBookingByEmployeeAndDate(employee, date).isPresent()) { - return ResponseEntity.status(HttpStatus.CONFLICT).build(); - } - - if (bookingService.getBookingByDateAndPlace(date, place).isPresent()) { - return ResponseEntity.status(HttpStatus.CONFLICT).build(); - } - - bookingService.createBooking(employee, place, date); - return ResponseEntity.status(HttpStatus.CREATED).build(); - - } catch (Exception e) { - return ResponseEntity.status(HttpStatus.BAD_REQUEST).build(); - } + @GetMapping("/{code}/book") + @ResponseStatus(code = HttpStatus.CREATED) + public void create(@PathVariable String code, @RequestBody BookingCreateDto bookingCreateDto) { + bookingService.create(code, bookingCreateDto); } } diff --git a/src/main/java/com/example/nto/controller/EmployeeController.java b/src/main/java/com/example/nto/controller/EmployeeController.java index 527340c..ada1246 100644 --- a/src/main/java/com/example/nto/controller/EmployeeController.java +++ b/src/main/java/com/example/nto/controller/EmployeeController.java @@ -1,30 +1,27 @@ package com.example.nto.controller; -import com.example.nto.entity.Employee; +import com.example.nto.controller.dto.EmployeeDto; import com.example.nto.service.EmployeeService; +import lombok.RequiredArgsConstructor; import org.springframework.http.HttpStatus; -import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.*; -import java.util.Optional; @RestController -@RequestMapping("/api/employee") +@RequestMapping("api") +@RequiredArgsConstructor public class EmployeeController { - private final EmployeeService employeeService; - public EmployeeController(EmployeeService employeeService) { - this.employeeService = employeeService; + @GetMapping("/{code}/auth") + @ResponseStatus(code = HttpStatus.OK) + public void login(@PathVariable String code) { + employeeService.auth(code); } - @GetMapping("/{code}") - public ResponseEntity getEmployeeByCode(@PathVariable String code) { - Optional employeeOpt = employeeService.findByCode(code); - if (employeeOpt.isPresent()) { - return ResponseEntity.ok(employeeOpt.get()); - } else { - return ResponseEntity.status(HttpStatus.NOT_FOUND).build(); - } + @GetMapping("/{code}/info") + @ResponseStatus(code = HttpStatus.OK) + public EmployeeDto getByCode(@PathVariable String code) { + return employeeService.getByCode(code); } } diff --git a/src/main/java/com/example/nto/controller/dto/BookingCreateDto.java b/src/main/java/com/example/nto/controller/dto/BookingCreateDto.java new file mode 100644 index 0000000..6bc3e66 --- /dev/null +++ b/src/main/java/com/example/nto/controller/dto/BookingCreateDto.java @@ -0,0 +1,18 @@ +package com.example.nto.controller.dto; + +import jakarta.validation.constraints.NotNull; +import jakarta.validation.constraints.Positive; +import lombok.*; + +import java.time.LocalDate; + +@Data +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class BookingCreateDto { + @NotNull + private LocalDate date; + @Positive + private long placeId; +} diff --git a/src/main/java/com/example/nto/controller/dto/EmployeeDto.java b/src/main/java/com/example/nto/controller/dto/EmployeeDto.java new file mode 100644 index 0000000..645dd3c --- /dev/null +++ b/src/main/java/com/example/nto/controller/dto/EmployeeDto.java @@ -0,0 +1,32 @@ +package com.example.nto.controller.dto; + + +import com.example.nto.entity.Booking; +import com.example.nto.entity.Employee; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.time.LocalDate; +import java.util.Map; +import java.util.TreeMap; + +@Data +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class EmployeeDto { + private String name; + private String photoUrl; + private Map booking; + + public static EmployeeDto toDto(Employee employee) { + Map dtoTreeMap = new TreeMap<>(); + for (Booking booking : employee.getBookingList()) { + dtoTreeMap.put(booking.getDate(), PlaceDto.toDto(booking.getPlace())); + } + + return new EmployeeDto(employee.getName(), employee.getPhotoUrl(), dtoTreeMap); + } +} \ No newline at end of file diff --git a/src/main/java/com/example/nto/controller/dto/PlaceDto.java b/src/main/java/com/example/nto/controller/dto/PlaceDto.java new file mode 100644 index 0000000..ac67aba --- /dev/null +++ b/src/main/java/com/example/nto/controller/dto/PlaceDto.java @@ -0,0 +1,18 @@ +package com.example.nto.controller.dto; + +import com.example.nto.entity.Place; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +@Data +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class PlaceDto { + private long id; + private String place; + + public static PlaceDto toDto(Place place){return new PlaceDto(place.getId(), place.getPlace());} +} diff --git a/src/main/java/com/example/nto/entity/Booking.java b/src/main/java/com/example/nto/entity/Booking.java index d4dd146..d0e04ba 100644 --- a/src/main/java/com/example/nto/entity/Booking.java +++ b/src/main/java/com/example/nto/entity/Booking.java @@ -1,42 +1,35 @@ package com.example.nto.entity; -import com.fasterxml.jackson.annotation.JsonIgnoreProperties; import jakarta.persistence.*; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + import java.time.LocalDate; +@Data @Entity +@Builder +@NoArgsConstructor +@AllArgsConstructor @Table(name = "booking") -@JsonIgnoreProperties({"hibernateLazyInitializer"}) public class Booking { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private long id; + @Column(name = "date") private LocalDate date; - @ManyToOne(fetch = FetchType.EAGER) + @ManyToOne(targetEntity = Place.class, fetch = FetchType.LAZY) @JoinColumn(name = "place_id") private Place place; - @ManyToOne(fetch = FetchType.EAGER) + @ManyToOne(fetch = FetchType.LAZY) @JoinColumn(name = "employee_id") private Employee employee; - public Booking() {} - public Booking(Employee employee, Place place, LocalDate date) { - this.employee = employee; - this.place = place; - this.date = date; - } - - 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 b1a34ef..6bf2fa1 100644 --- a/src/main/java/com/example/nto/entity/Employee.java +++ b/src/main/java/com/example/nto/entity/Employee.java @@ -1,11 +1,18 @@ package com.example.nto.entity; -import com.fasterxml.jackson.annotation.JsonIgnore; import jakarta.persistence.*; -import java.util.ArrayList; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + import java.util.List; +@Data @Entity +@Builder +@NoArgsConstructor +@AllArgsConstructor @Table(name = "employee") public class Employee { @@ -13,33 +20,16 @@ public class Employee { @GeneratedValue(strategy = GenerationType.IDENTITY) private long id; + @Column(name = "name") private String name; - @Column(unique = true) + @Column(name = "code") private String code; + @Column(name = "photo_url") private String photoUrl; @OneToMany(mappedBy = "employee", cascade = CascadeType.ALL, fetch = FetchType.LAZY) - @JsonIgnore - private List bookingList = new ArrayList<>(); + private List bookingList; - public Employee() {} - - public Employee(String name, String code, String photoUrl) { - this.name = name; - this.code = code; - this.photoUrl = photoUrl; - } - - public long getId() { return id; } - public void setId(long id) { this.id = id; } - public String getName() { return name; } - public void setName(String name) { this.name = name; } - public String getCode() { return code; } - public void setCode(String code) { this.code = code; } - public String getPhotoUrl() { return photoUrl; } - public void setPhotoUrl(String photoUrl) { this.photoUrl = photoUrl; } - public List getBookingList() { return bookingList; } - 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 0981a3a..c266212 100644 --- a/src/main/java/com/example/nto/entity/Place.java +++ b/src/main/java/com/example/nto/entity/Place.java @@ -1,23 +1,23 @@ package com.example.nto.entity; import jakarta.persistence.*; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; +@Data @Entity +@Builder +@NoArgsConstructor +@AllArgsConstructor @Table(name = "place") public class Place { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) - private Long id; + private long id; - @Column(name = "place_name", nullable = false, unique = true) + @Column(name = "place_name") private String place; - - public Place() {} - public Place(String place) { this.place = place; } - - public Long getId() { return id; } - public void setId(Long id) { this.id = id; } - public String getPlace() { return place; } - public void setPlace(String place) { this.place = place; } } diff --git a/src/main/java/com/example/nto/exception/BookingAlreadyExistException.java b/src/main/java/com/example/nto/exception/BookingAlreadyExistException.java new file mode 100644 index 0000000..2748341 --- /dev/null +++ b/src/main/java/com/example/nto/exception/BookingAlreadyExistException.java @@ -0,0 +1,7 @@ +package com.example.nto.exception; + +public class BookingAlreadyExistException extends RuntimeException { + public BookingAlreadyExistException(String message) { + super(message); + } +} diff --git a/src/main/java/com/example/nto/exception/EmployeeNotFoundException.java b/src/main/java/com/example/nto/exception/EmployeeNotFoundException.java new file mode 100644 index 0000000..d427077 --- /dev/null +++ b/src/main/java/com/example/nto/exception/EmployeeNotFoundException.java @@ -0,0 +1,7 @@ +package com.example.nto.exception; + +public class EmployeeNotFoundException extends RuntimeException { + public EmployeeNotFoundException(String message) { + super(message); + } +} diff --git a/src/main/java/com/example/nto/exception/PlaceNotFoundException.java b/src/main/java/com/example/nto/exception/PlaceNotFoundException.java new file mode 100644 index 0000000..2560027 --- /dev/null +++ b/src/main/java/com/example/nto/exception/PlaceNotFoundException.java @@ -0,0 +1,7 @@ +package com.example.nto.exception; + +public class PlaceNotFoundException extends RuntimeException { + public PlaceNotFoundException(String message) { + super(message); + } +} diff --git a/src/main/java/com/example/nto/exception/handler/GlobalExceptionHandler.java b/src/main/java/com/example/nto/exception/handler/GlobalExceptionHandler.java new file mode 100644 index 0000000..c2c496f --- /dev/null +++ b/src/main/java/com/example/nto/exception/handler/GlobalExceptionHandler.java @@ -0,0 +1,35 @@ +package com.example.nto.exception.handler; + +import com.example.nto.exception.BookingAlreadyExistException; +import com.example.nto.exception.EmployeeNotFoundException; +import com.example.nto.exception.PlaceNotFoundException; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.ControllerAdvice; +import org.springframework.web.bind.annotation.ExceptionHandler; + +@ControllerAdvice +public class GlobalExceptionHandler { + @ExceptionHandler(EmployeeNotFoundException.class) + public ResponseEntity handleEmployeeNotFoundException(EmployeeNotFoundException e) { + return new ResponseEntity<>(e.getMessage(), HttpStatus.UNAUTHORIZED); + } + + @ExceptionHandler(BookingAlreadyExistException.class) + public ResponseEntity handleBookingAlreadyExistException(BookingAlreadyExistException e) { + return new ResponseEntity<>(e.getMessage(), HttpStatus.CONFLICT); + } + + @ExceptionHandler(PlaceNotFoundException.class) + public ResponseEntity handlePlaceNotFoundException(PlaceNotFoundException e) { + return new ResponseEntity<>(e.getMessage(), HttpStatus.BAD_REQUEST); + } + + @ExceptionHandler(Exception.class) + public ResponseEntity handlerGenericException(Exception e) { + return new ResponseEntity<>(e.getMessage(), HttpStatus.BAD_REQUEST); + } + + + +} diff --git a/src/main/java/com/example/nto/repository/BookingRepository.java b/src/main/java/com/example/nto/repository/BookingRepository.java index c6fc3eb..7d4093b 100644 --- a/src/main/java/com/example/nto/repository/BookingRepository.java +++ b/src/main/java/com/example/nto/repository/BookingRepository.java @@ -10,9 +10,9 @@ import java.util.List; import java.util.Optional; public interface BookingRepository extends JpaRepository { - List findByEmployee(Employee employee); - List findByDate(LocalDate date); + List findByDateBetween(LocalDate start, LocalDate end); + Optional findByDateAndPlace(LocalDate date, Place place); - Optional findByEmployeeAndDate(Employee employee, LocalDate date); + Optional findByDateAndEmployee(LocalDate date, Employee employee); } diff --git a/src/main/java/com/example/nto/repository/EmployeeRepository.java b/src/main/java/com/example/nto/repository/EmployeeRepository.java index 1a4f77a..d845a04 100644 --- a/src/main/java/com/example/nto/repository/EmployeeRepository.java +++ b/src/main/java/com/example/nto/repository/EmployeeRepository.java @@ -1,9 +1,12 @@ package com.example.nto.repository; import com.example.nto.entity.Employee; +import org.springframework.data.jpa.repository.EntityGraph; import org.springframework.data.jpa.repository.JpaRepository; + import java.util.Optional; public interface EmployeeRepository extends JpaRepository { + @EntityGraph(attributePaths = {"bookingList", "bookingList.place"}) Optional findByCode(String code); } diff --git a/src/main/java/com/example/nto/service/BookingService.java b/src/main/java/com/example/nto/service/BookingService.java index fe191ea..a382837 100644 --- a/src/main/java/com/example/nto/service/BookingService.java +++ b/src/main/java/com/example/nto/service/BookingService.java @@ -1,19 +1,15 @@ package com.example.nto.service; +import com.example.nto.controller.dto.BookingCreateDto; +import com.example.nto.controller.dto.PlaceDto; 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; -import java.util.Optional; +import java.util.Map; public interface BookingService { - List getBookingsByEmployee(Employee employee); - List getBookingsByDate(LocalDate date); - Optional getBookingByDateAndPlace(LocalDate date, Place place); + Map> getFreePLace(String code); - Optional getBookingByEmployeeAndDate(Employee employee, LocalDate date); - - Booking createBooking(Employee employee, Place place, LocalDate date); + Booking create(String code, BookingCreateDto bookingCreateDto); } diff --git a/src/main/java/com/example/nto/service/EmployeeService.java b/src/main/java/com/example/nto/service/EmployeeService.java index a300e34..e5b41a4 100644 --- a/src/main/java/com/example/nto/service/EmployeeService.java +++ b/src/main/java/com/example/nto/service/EmployeeService.java @@ -1,8 +1,8 @@ package com.example.nto.service; -import com.example.nto.entity.Employee; -import java.util.Optional; +import com.example.nto.controller.dto.EmployeeDto; public interface EmployeeService { - Optional findByCode(String code); -} + EmployeeDto getByCode(String code); + void auth(String code); +} \ No newline at end of file diff --git a/src/main/java/com/example/nto/service/PlaceService.java b/src/main/java/com/example/nto/service/PlaceService.java deleted file mode 100644 index 37e28ac..0000000 --- a/src/main/java/com/example/nto/service/PlaceService.java +++ /dev/null @@ -1,10 +0,0 @@ -package com.example.nto.service; - -import com.example.nto.entity.Place; - -import java.util.List; - -public interface PlaceService { - List getAllPlaces(); - Place getPlaceById(Long id); -} 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 2091f53..583a7ba 100644 --- a/src/main/java/com/example/nto/service/impl/BookingServiceImpl.java +++ b/src/main/java/com/example/nto/service/impl/BookingServiceImpl.java @@ -1,51 +1,102 @@ package com.example.nto.service.impl; +import com.example.nto.controller.dto.BookingCreateDto; +import com.example.nto.controller.dto.PlaceDto; import com.example.nto.entity.Booking; import com.example.nto.entity.Employee; import com.example.nto.entity.Place; +import com.example.nto.exception.BookingAlreadyExistException; +import com.example.nto.exception.EmployeeNotFoundException; +import com.example.nto.exception.PlaceNotFoundException; 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 com.example.nto.service.EmployeeService; +import lombok.RequiredArgsConstructor; +import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; import java.time.LocalDate; -import java.util.List; -import java.util.Optional; +import java.time.ZoneId; +import java.util.*; +import java.util.stream.Collectors; @Service +@RequiredArgsConstructor public class BookingServiceImpl implements BookingService { private final BookingRepository bookingRepository; + private final EmployeeRepository employeeRepository; + private final PlaceRepository placeRepository; + private final EmployeeService employeeService; - public BookingServiceImpl(BookingRepository bookingRepository) { - this.bookingRepository = bookingRepository; + @Value("${booking.days-ahead}") + private int daysAhead; + + @Override + @Transactional(readOnly = true) + public Map> getFreePLace(String code) { + employeeService.auth(code); + + List allPlaces = placeRepository.findAll(); + + LocalDate today = LocalDate.now(ZoneId.systemDefault()); + LocalDate end = today.plusDays(daysAhead); + + List bookings = bookingRepository.findByDateBetween(today, end); + + Map> busyByDate = bookings.stream() + .collect(Collectors.groupingBy(Booking::getDate, Collectors.mapping(b -> b.getPlace().getId(), Collectors.toSet()))); + + + Map> result = new LinkedHashMap<>(); + + for (int i = 0; i <= daysAhead; i++){ + LocalDate currentDate = today.plusDays(i); + Set busyPlaces = busyByDate.getOrDefault(currentDate, Collections.emptySet()); + + List freePlaces = allPlaces.stream() + .filter(place -> busyPlaces.contains(place.getId())) + .map(place -> new PlaceDto(place.getId(), place.getPlace())) + .toList(); + result.put(currentDate, freePlaces); + } + + return result; } @Override - public List getBookingsByEmployee(Employee employee) { - return bookingRepository.findByEmployee(employee); - } + @Transactional + public Booking create(String code, BookingCreateDto bookingCreateDto) { + LocalDate date = bookingCreateDto.getDate(); + LocalDate today = LocalDate.now(ZoneId.systemDefault()); + if (date.isBefore(today) || date.isAfter(today.plusDays(daysAhead))) { + throw new IllegalArgumentException("Date is out of booking window"); + } - @Override - public List getBookingsByDate(LocalDate date) { - return bookingRepository.findByDate(date); - } + Employee employee = employeeRepository.findByCode(code) + .orElseThrow(() -> new EmployeeNotFoundException("Employee with " + code + " code not found!")); - @Override - public Optional getBookingByDateAndPlace(LocalDate date, Place place) { - return bookingRepository.findByDateAndPlace(date, place); - } + long placeId = bookingCreateDto.getPlaceId(); + Place place = placeRepository.findById(placeId) + .orElseThrow(() -> new PlaceNotFoundException("Place with " + placeId + " id not found!")); - @Override - public Optional getBookingByEmployeeAndDate(Employee employee, LocalDate date) { - return bookingRepository.findByEmployeeAndDate(employee, date); - } + if (bookingRepository.findByDateAndPlace(date, place).isPresent()) { + throw new BookingAlreadyExistException("Booking already exists"); + } + + if (bookingRepository.findByDateAndEmployee(date, employee).isPresent()) { + throw new BookingAlreadyExistException("This employee already has another booking on " + date); + } + + Booking booking = Booking.builder() + .date(date) + .employee(employee) + .place(place) + .build(); - @Override - public Booking createBooking(Employee employee, Place place, LocalDate date) { - Booking booking = new Booking(); - booking.setEmployee(employee); - booking.setPlace(place); - booking.setDate(date); return 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 6939037..07cd2af 100644 --- a/src/main/java/com/example/nto/service/impl/EmployeeServiceImpl.java +++ b/src/main/java/com/example/nto/service/impl/EmployeeServiceImpl.java @@ -1,22 +1,31 @@ package com.example.nto.service.impl; -import com.example.nto.entity.Employee; +import com.example.nto.controller.dto.EmployeeDto; +import com.example.nto.exception.EmployeeNotFoundException; import com.example.nto.repository.EmployeeRepository; import com.example.nto.service.EmployeeService; +import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Service; -import java.util.Optional; +import org.springframework.transaction.annotation.Transactional; + @Service +@RequiredArgsConstructor public class EmployeeServiceImpl implements EmployeeService { - private final EmployeeRepository employeeRepository; - public EmployeeServiceImpl(EmployeeRepository employeeRepository) { - this.employeeRepository = employeeRepository; + @Override + @Transactional(readOnly = true) + public EmployeeDto getByCode(String code) { + return employeeRepository.findByCode(code).map(EmployeeDto::toDto) + .orElseThrow(() -> new EmployeeNotFoundException(("Employee with " + code + " code not found!"))); } @Override - public Optional findByCode(String code) { - return employeeRepository.findByCode(code); + @Transactional(readOnly = true) + public void auth(String code) { + if (employeeRepository.findByCode(code).isEmpty()) { + throw new EmployeeNotFoundException("Employee with " + code + " code not found!"); + } } -} +} \ No newline at end of file diff --git a/src/main/java/com/example/nto/service/impl/PlaceServiceImpl.java b/src/main/java/com/example/nto/service/impl/PlaceServiceImpl.java deleted file mode 100644 index 3ab1cb1..0000000 --- a/src/main/java/com/example/nto/service/impl/PlaceServiceImpl.java +++ /dev/null @@ -1,29 +0,0 @@ -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 org.springframework.stereotype.Service; - -import java.util.List; - -@Service -public class PlaceServiceImpl implements PlaceService { - - private final PlaceRepository placeRepository; - - public PlaceServiceImpl(PlaceRepository placeRepository) { - this.placeRepository = placeRepository; - } - - @Override - public List getAllPlaces() { - return placeRepository.findAll(); - } - - @Override - public Place getPlaceById(Long id) { - return placeRepository.findById(id) - .orElseThrow(() -> new RuntimeException("Place not found")); - } -} -- 2.34.1