From 933bd6b7fdec22319ad20e699bf8f8f2371e8322 Mon Sep 17 00:00:00 2001 From: Harkamal Randhawa Date: Tue, 14 Apr 2026 20:02:03 -0600 Subject: [PATCH] Add species filtering --- .../backend/controller/ServiceController.java | 3 ++- .../backend/repository/ServiceRepository.java | 8 ++++---- .../backend/service/AppointmentService.java | 15 +++++++++++++++ .../petshop/backend/service/ServiceService.java | 12 ++++-------- 4 files changed, 25 insertions(+), 13 deletions(-) diff --git a/backend/src/main/java/com/petshop/backend/controller/ServiceController.java b/backend/src/main/java/com/petshop/backend/controller/ServiceController.java index 4f6503dc..4789321a 100644 --- a/backend/src/main/java/com/petshop/backend/controller/ServiceController.java +++ b/backend/src/main/java/com/petshop/backend/controller/ServiceController.java @@ -25,8 +25,9 @@ public class ServiceController { @GetMapping public ResponseEntity> getAllServices( @RequestParam(required = false) String q, + @RequestParam(required = false) String species, Pageable pageable) { - return ResponseEntity.ok(serviceService.getAllServices(q, pageable)); + return ResponseEntity.ok(serviceService.getAllServices(q, species, pageable)); } @GetMapping("/{id}") diff --git a/backend/src/main/java/com/petshop/backend/repository/ServiceRepository.java b/backend/src/main/java/com/petshop/backend/repository/ServiceRepository.java index 7b057856..e6271dfd 100644 --- a/backend/src/main/java/com/petshop/backend/repository/ServiceRepository.java +++ b/backend/src/main/java/com/petshop/backend/repository/ServiceRepository.java @@ -11,8 +11,8 @@ import org.springframework.stereotype.Repository; @Repository public interface ServiceRepository extends JpaRepository { - @Query("SELECT s FROM Service s WHERE " + - "LOWER(s.serviceName) LIKE LOWER(CONCAT('%', :q, '%')) OR " + - "LOWER(s.serviceDesc) LIKE LOWER(CONCAT('%', :q, '%'))") - Page searchServices(@Param("q") String query, Pageable pageable); + @Query("SELECT DISTINCT s FROM Service s LEFT JOIN s.species sp WHERE " + + "(:q IS NULL OR LOWER(s.serviceName) LIKE LOWER(CONCAT('%', :q, '%')) OR LOWER(COALESCE(s.serviceDesc, '')) LIKE LOWER(CONCAT('%', :q, '%'))) AND " + + "(:species IS NULL OR LOWER(sp) = LOWER(:species))") + Page searchServices(@Param("q") String q, @Param("species") String species, Pageable pageable); } diff --git a/backend/src/main/java/com/petshop/backend/service/AppointmentService.java b/backend/src/main/java/com/petshop/backend/service/AppointmentService.java index 45edd69f..b8b4ba60 100644 --- a/backend/src/main/java/com/petshop/backend/service/AppointmentService.java +++ b/backend/src/main/java/com/petshop/backend/service/AppointmentService.java @@ -8,6 +8,7 @@ import com.petshop.backend.entity.Appointment; import com.petshop.backend.entity.Pet; import com.petshop.backend.entity.StoreLocation; import com.petshop.backend.entity.User; +import com.petshop.backend.exception.BusinessException; import com.petshop.backend.exception.ResourceNotFoundException; import com.petshop.backend.repository.AdoptionRepository; import com.petshop.backend.repository.AppointmentRepository; @@ -28,6 +29,7 @@ import java.time.LocalDateTime; import java.time.LocalTime; import java.util.ArrayList; import java.util.List; +import java.util.Set; import java.util.stream.Collectors; @Service @@ -108,6 +110,7 @@ public class AppointmentService { User employee = resolveAppointmentEmployee(request.getEmployeeId(), store.getStoreId()); validateStoreAccess(store.getStoreId(), authenticatedUser); + validatePetServiceCompatibility(pet, service); validateAvailability(employee, service, request.getAppointmentDate(), request.getAppointmentTime(), null); Appointment appointment = new Appointment(); @@ -147,6 +150,7 @@ public class AppointmentService { User employee = resolveAppointmentEmployee(request.getEmployeeId(), store.getStoreId()); validateStoreAccess(store.getStoreId(), authenticatedUser); + validatePetServiceCompatibility(pet, service); validateAvailability(employee, service, request.getAppointmentDate(), request.getAppointmentTime(), id); appointment.setCustomer(customer); @@ -254,6 +258,17 @@ public class AppointmentService { return trimmed.isEmpty() ? null : trimmed; } + private void validatePetServiceCompatibility(Pet pet, com.petshop.backend.entity.Service service) { + if (pet == null) return; + Set allowed = service.getSpecies(); + if (allowed == null || allowed.isEmpty()) return; + boolean compatible = allowed.stream().anyMatch(s -> s.equalsIgnoreCase(pet.getPetSpecies())); + if (!compatible) { + throw new BusinessException( + "Service \"" + service.getServiceName() + "\" is not available for " + pet.getPetSpecies()); + } + } + private void validateAppointmentRequest(AppointmentRequest request) { if ("Booked".equalsIgnoreCase(request.getAppointmentStatus())) { LocalDateTime appointmentDateTime = LocalDateTime.of(request.getAppointmentDate(), request.getAppointmentTime()); diff --git a/backend/src/main/java/com/petshop/backend/service/ServiceService.java b/backend/src/main/java/com/petshop/backend/service/ServiceService.java index 65166f44..ccd7b829 100644 --- a/backend/src/main/java/com/petshop/backend/service/ServiceService.java +++ b/backend/src/main/java/com/petshop/backend/service/ServiceService.java @@ -20,14 +20,10 @@ public class ServiceService { } @Transactional(readOnly = true) - public Page getAllServices(String query, Pageable pageable) { - Page services; - if (query != null && !query.trim().isEmpty()) { - services = serviceRepository.searchServices(query, pageable); - } else { - services = serviceRepository.findAll(pageable); - } - return services.map(this::mapToResponse); + public Page getAllServices(String query, String species, Pageable pageable) { + String q = (query != null && !query.trim().isEmpty()) ? query.trim() : null; + String sp = (species != null && !species.trim().isEmpty()) ? species.trim() : null; + return serviceRepository.searchServices(q, sp, pageable).map(this::mapToResponse); } @Transactional(readOnly = true)