diff --git a/backend/src/main/java/com/petshop/backend/controller/AdoptionController.java b/backend/src/main/java/com/petshop/backend/controller/AdoptionController.java new file mode 100644 index 00000000..0efe8741 --- /dev/null +++ b/backend/src/main/java/com/petshop/backend/controller/AdoptionController.java @@ -0,0 +1,55 @@ +package com.petshop.backend.controller; + +import com.petshop.backend.dto.adoption.AdoptionRequest; +import com.petshop.backend.dto.adoption.AdoptionResponse; +import com.petshop.backend.dto.common.BulkDeleteRequest; +import com.petshop.backend.service.AdoptionService; +import jakarta.validation.Valid; +import lombok.RequiredArgsConstructor; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.Pageable; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.*; + +@RestController +@RequestMapping("/api/v1/adoptions") +@RequiredArgsConstructor +public class AdoptionController { + + private final AdoptionService adoptionService; + + @GetMapping + public ResponseEntity> getAllAdoptions(Pageable pageable) { + return ResponseEntity.ok(adoptionService.getAllAdoptions(pageable)); + } + + @GetMapping("/{id}") + public ResponseEntity getAdoptionById(@PathVariable Long id) { + return ResponseEntity.ok(adoptionService.getAdoptionById(id)); + } + + @PostMapping + public ResponseEntity createAdoption(@Valid @RequestBody AdoptionRequest request) { + return ResponseEntity.status(HttpStatus.CREATED).body(adoptionService.createAdoption(request)); + } + + @PutMapping("/{id}") + public ResponseEntity updateAdoption( + @PathVariable Long id, + @Valid @RequestBody AdoptionRequest request) { + return ResponseEntity.ok(adoptionService.updateAdoption(id, request)); + } + + @DeleteMapping("/{id}") + public ResponseEntity deleteAdoption(@PathVariable Long id) { + adoptionService.deleteAdoption(id); + return ResponseEntity.noContent().build(); + } + + @PostMapping("/bulk-delete") + public ResponseEntity bulkDeleteAdoptions(@Valid @RequestBody BulkDeleteRequest request) { + adoptionService.bulkDeleteAdoptions(request); + return ResponseEntity.noContent().build(); + } +} diff --git a/backend/src/main/java/com/petshop/backend/controller/AppointmentController.java b/backend/src/main/java/com/petshop/backend/controller/AppointmentController.java new file mode 100644 index 00000000..843aafe1 --- /dev/null +++ b/backend/src/main/java/com/petshop/backend/controller/AppointmentController.java @@ -0,0 +1,55 @@ +package com.petshop.backend.controller; + +import com.petshop.backend.dto.appointment.AppointmentRequest; +import com.petshop.backend.dto.appointment.AppointmentResponse; +import com.petshop.backend.dto.common.BulkDeleteRequest; +import com.petshop.backend.service.AppointmentService; +import jakarta.validation.Valid; +import lombok.RequiredArgsConstructor; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.Pageable; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.*; + +@RestController +@RequestMapping("/api/v1/appointments") +@RequiredArgsConstructor +public class AppointmentController { + + private final AppointmentService appointmentService; + + @GetMapping + public ResponseEntity> getAllAppointments(Pageable pageable) { + return ResponseEntity.ok(appointmentService.getAllAppointments(pageable)); + } + + @GetMapping("/{id}") + public ResponseEntity getAppointmentById(@PathVariable Long id) { + return ResponseEntity.ok(appointmentService.getAppointmentById(id)); + } + + @PostMapping + public ResponseEntity createAppointment(@Valid @RequestBody AppointmentRequest request) { + return ResponseEntity.status(HttpStatus.CREATED).body(appointmentService.createAppointment(request)); + } + + @PutMapping("/{id}") + public ResponseEntity updateAppointment( + @PathVariable Long id, + @Valid @RequestBody AppointmentRequest request) { + return ResponseEntity.ok(appointmentService.updateAppointment(id, request)); + } + + @DeleteMapping("/{id}") + public ResponseEntity deleteAppointment(@PathVariable Long id) { + appointmentService.deleteAppointment(id); + return ResponseEntity.noContent().build(); + } + + @PostMapping("/bulk-delete") + public ResponseEntity bulkDeleteAppointments(@Valid @RequestBody BulkDeleteRequest request) { + appointmentService.bulkDeleteAppointments(request); + return ResponseEntity.noContent().build(); + } +} diff --git a/backend/src/main/java/com/petshop/backend/controller/CategoryController.java b/backend/src/main/java/com/petshop/backend/controller/CategoryController.java new file mode 100644 index 00000000..7c6ab6be --- /dev/null +++ b/backend/src/main/java/com/petshop/backend/controller/CategoryController.java @@ -0,0 +1,57 @@ +package com.petshop.backend.controller; + +import com.petshop.backend.dto.category.CategoryRequest; +import com.petshop.backend.dto.category.CategoryResponse; +import com.petshop.backend.dto.common.BulkDeleteRequest; +import com.petshop.backend.service.CategoryService; +import jakarta.validation.Valid; +import lombok.RequiredArgsConstructor; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.Pageable; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.*; + +@RestController +@RequestMapping("/api/v1/categories") +@RequiredArgsConstructor +public class CategoryController { + + private final CategoryService categoryService; + + @GetMapping + public ResponseEntity> getAllCategories( + @RequestParam(required = false) String q, + Pageable pageable) { + return ResponseEntity.ok(categoryService.getAllCategories(q, pageable)); + } + + @GetMapping("/{id}") + public ResponseEntity getCategoryById(@PathVariable Long id) { + return ResponseEntity.ok(categoryService.getCategoryById(id)); + } + + @PostMapping + public ResponseEntity createCategory(@Valid @RequestBody CategoryRequest request) { + return ResponseEntity.status(HttpStatus.CREATED).body(categoryService.createCategory(request)); + } + + @PutMapping("/{id}") + public ResponseEntity updateCategory( + @PathVariable Long id, + @Valid @RequestBody CategoryRequest request) { + return ResponseEntity.ok(categoryService.updateCategory(id, request)); + } + + @DeleteMapping("/{id}") + public ResponseEntity deleteCategory(@PathVariable Long id) { + categoryService.deleteCategory(id); + return ResponseEntity.noContent().build(); + } + + @PostMapping("/bulk-delete") + public ResponseEntity bulkDeleteCategories(@Valid @RequestBody BulkDeleteRequest request) { + categoryService.bulkDeleteCategories(request); + return ResponseEntity.noContent().build(); + } +} diff --git a/backend/src/main/java/com/petshop/backend/controller/CustomerController.java b/backend/src/main/java/com/petshop/backend/controller/CustomerController.java new file mode 100644 index 00000000..393d043f --- /dev/null +++ b/backend/src/main/java/com/petshop/backend/controller/CustomerController.java @@ -0,0 +1,57 @@ +package com.petshop.backend.controller; + +import com.petshop.backend.dto.common.BulkDeleteRequest; +import com.petshop.backend.dto.customer.CustomerRequest; +import com.petshop.backend.dto.customer.CustomerResponse; +import com.petshop.backend.service.CustomerService; +import jakarta.validation.Valid; +import lombok.RequiredArgsConstructor; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.Pageable; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.*; + +@RestController +@RequestMapping("/api/v1/customers") +@RequiredArgsConstructor +public class CustomerController { + + private final CustomerService customerService; + + @GetMapping + public ResponseEntity> getAllCustomers( + @RequestParam(required = false) String q, + Pageable pageable) { + return ResponseEntity.ok(customerService.getAllCustomers(q, pageable)); + } + + @GetMapping("/{id}") + public ResponseEntity getCustomerById(@PathVariable Long id) { + return ResponseEntity.ok(customerService.getCustomerById(id)); + } + + @PostMapping + public ResponseEntity createCustomer(@Valid @RequestBody CustomerRequest request) { + return ResponseEntity.status(HttpStatus.CREATED).body(customerService.createCustomer(request)); + } + + @PutMapping("/{id}") + public ResponseEntity updateCustomer( + @PathVariable Long id, + @Valid @RequestBody CustomerRequest request) { + return ResponseEntity.ok(customerService.updateCustomer(id, request)); + } + + @DeleteMapping("/{id}") + public ResponseEntity deleteCustomer(@PathVariable Long id) { + customerService.deleteCustomer(id); + return ResponseEntity.noContent().build(); + } + + @PostMapping("/bulk-delete") + public ResponseEntity bulkDeleteCustomers(@Valid @RequestBody BulkDeleteRequest request) { + customerService.bulkDeleteCustomers(request); + return ResponseEntity.noContent().build(); + } +} diff --git a/backend/src/main/java/com/petshop/backend/controller/PetController.java b/backend/src/main/java/com/petshop/backend/controller/PetController.java new file mode 100644 index 00000000..f5c45015 --- /dev/null +++ b/backend/src/main/java/com/petshop/backend/controller/PetController.java @@ -0,0 +1,57 @@ +package com.petshop.backend.controller; + +import com.petshop.backend.dto.common.BulkDeleteRequest; +import com.petshop.backend.dto.pet.PetRequest; +import com.petshop.backend.dto.pet.PetResponse; +import com.petshop.backend.service.PetService; +import jakarta.validation.Valid; +import lombok.RequiredArgsConstructor; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.Pageable; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.*; + +@RestController +@RequestMapping("/api/v1/pets") +@RequiredArgsConstructor +public class PetController { + + private final PetService petService; + + @GetMapping + public ResponseEntity> getAllPets( + @RequestParam(required = false) String q, + Pageable pageable) { + return ResponseEntity.ok(petService.getAllPets(q, pageable)); + } + + @GetMapping("/{id}") + public ResponseEntity getPetById(@PathVariable Long id) { + return ResponseEntity.ok(petService.getPetById(id)); + } + + @PostMapping + public ResponseEntity createPet(@Valid @RequestBody PetRequest request) { + return ResponseEntity.status(HttpStatus.CREATED).body(petService.createPet(request)); + } + + @PutMapping("/{id}") + public ResponseEntity updatePet( + @PathVariable Long id, + @Valid @RequestBody PetRequest request) { + return ResponseEntity.ok(petService.updatePet(id, request)); + } + + @DeleteMapping("/{id}") + public ResponseEntity deletePet(@PathVariable Long id) { + petService.deletePet(id); + return ResponseEntity.noContent().build(); + } + + @PostMapping("/bulk-delete") + public ResponseEntity bulkDeletePets(@Valid @RequestBody BulkDeleteRequest request) { + petService.bulkDeletePets(request); + return ResponseEntity.noContent().build(); + } +} diff --git a/backend/src/main/java/com/petshop/backend/controller/ProductController.java b/backend/src/main/java/com/petshop/backend/controller/ProductController.java new file mode 100644 index 00000000..7cec17a8 --- /dev/null +++ b/backend/src/main/java/com/petshop/backend/controller/ProductController.java @@ -0,0 +1,57 @@ +package com.petshop.backend.controller; + +import com.petshop.backend.dto.common.BulkDeleteRequest; +import com.petshop.backend.dto.product.ProductRequest; +import com.petshop.backend.dto.product.ProductResponse; +import com.petshop.backend.service.ProductService; +import jakarta.validation.Valid; +import lombok.RequiredArgsConstructor; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.Pageable; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.*; + +@RestController +@RequestMapping("/api/v1/products") +@RequiredArgsConstructor +public class ProductController { + + private final ProductService productService; + + @GetMapping + public ResponseEntity> getAllProducts( + @RequestParam(required = false) String q, + Pageable pageable) { + return ResponseEntity.ok(productService.getAllProducts(q, pageable)); + } + + @GetMapping("/{id}") + public ResponseEntity getProductById(@PathVariable Long id) { + return ResponseEntity.ok(productService.getProductById(id)); + } + + @PostMapping + public ResponseEntity createProduct(@Valid @RequestBody ProductRequest request) { + return ResponseEntity.status(HttpStatus.CREATED).body(productService.createProduct(request)); + } + + @PutMapping("/{id}") + public ResponseEntity updateProduct( + @PathVariable Long id, + @Valid @RequestBody ProductRequest request) { + return ResponseEntity.ok(productService.updateProduct(id, request)); + } + + @DeleteMapping("/{id}") + public ResponseEntity deleteProduct(@PathVariable Long id) { + productService.deleteProduct(id); + return ResponseEntity.noContent().build(); + } + + @PostMapping("/bulk-delete") + public ResponseEntity bulkDeleteProducts(@Valid @RequestBody BulkDeleteRequest request) { + productService.bulkDeleteProducts(request); + return ResponseEntity.noContent().build(); + } +} diff --git a/backend/src/main/java/com/petshop/backend/controller/ServiceController.java b/backend/src/main/java/com/petshop/backend/controller/ServiceController.java new file mode 100644 index 00000000..e1c5415b --- /dev/null +++ b/backend/src/main/java/com/petshop/backend/controller/ServiceController.java @@ -0,0 +1,57 @@ +package com.petshop.backend.controller; + +import com.petshop.backend.dto.common.BulkDeleteRequest; +import com.petshop.backend.dto.service.ServiceRequest; +import com.petshop.backend.dto.service.ServiceResponse; +import com.petshop.backend.service.ServiceService; +import jakarta.validation.Valid; +import lombok.RequiredArgsConstructor; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.Pageable; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.*; + +@RestController +@RequestMapping("/api/v1/services") +@RequiredArgsConstructor +public class ServiceController { + + private final ServiceService serviceService; + + @GetMapping + public ResponseEntity> getAllServices( + @RequestParam(required = false) String q, + Pageable pageable) { + return ResponseEntity.ok(serviceService.getAllServices(q, pageable)); + } + + @GetMapping("/{id}") + public ResponseEntity getServiceById(@PathVariable Long id) { + return ResponseEntity.ok(serviceService.getServiceById(id)); + } + + @PostMapping + public ResponseEntity createService(@Valid @RequestBody ServiceRequest request) { + return ResponseEntity.status(HttpStatus.CREATED).body(serviceService.createService(request)); + } + + @PutMapping("/{id}") + public ResponseEntity updateService( + @PathVariable Long id, + @Valid @RequestBody ServiceRequest request) { + return ResponseEntity.ok(serviceService.updateService(id, request)); + } + + @DeleteMapping("/{id}") + public ResponseEntity deleteService(@PathVariable Long id) { + serviceService.deleteService(id); + return ResponseEntity.noContent().build(); + } + + @PostMapping("/bulk-delete") + public ResponseEntity bulkDeleteServices(@Valid @RequestBody BulkDeleteRequest request) { + serviceService.bulkDeleteServices(request); + return ResponseEntity.noContent().build(); + } +} diff --git a/backend/src/main/java/com/petshop/backend/exception/BusinessException.java b/backend/src/main/java/com/petshop/backend/exception/BusinessException.java new file mode 100644 index 00000000..005ee62b --- /dev/null +++ b/backend/src/main/java/com/petshop/backend/exception/BusinessException.java @@ -0,0 +1,7 @@ +package com.petshop.backend.exception; + +public class BusinessException extends RuntimeException { + public BusinessException(String message) { + super(message); + } +} diff --git a/backend/src/main/java/com/petshop/backend/exception/GlobalExceptionHandler.java b/backend/src/main/java/com/petshop/backend/exception/GlobalExceptionHandler.java new file mode 100644 index 00000000..290752dd --- /dev/null +++ b/backend/src/main/java/com/petshop/backend/exception/GlobalExceptionHandler.java @@ -0,0 +1,65 @@ +package com.petshop.backend.exception; + +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.validation.FieldError; +import org.springframework.web.bind.MethodArgumentNotValidException; +import org.springframework.web.bind.annotation.ExceptionHandler; +import org.springframework.web.bind.annotation.RestControllerAdvice; + +import java.time.LocalDateTime; +import java.util.HashMap; +import java.util.Map; + +@RestControllerAdvice +public class GlobalExceptionHandler { + + @ExceptionHandler(ResourceNotFoundException.class) + public ResponseEntity handleResourceNotFound(ResourceNotFoundException ex) { + ErrorResponse error = new ErrorResponse( + HttpStatus.NOT_FOUND.value(), + ex.getMessage(), + LocalDateTime.now() + ); + return ResponseEntity.status(HttpStatus.NOT_FOUND).body(error); + } + + @ExceptionHandler(BusinessException.class) + public ResponseEntity handleBusinessException(BusinessException ex) { + ErrorResponse error = new ErrorResponse( + HttpStatus.BAD_REQUEST.value(), + ex.getMessage(), + LocalDateTime.now() + ); + return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(error); + } + + @ExceptionHandler(MethodArgumentNotValidException.class) + public ResponseEntity> handleValidationExceptions(MethodArgumentNotValidException ex) { + Map errors = new HashMap<>(); + ex.getBindingResult().getAllErrors().forEach((error) -> { + String fieldName = ((FieldError) error).getField(); + String errorMessage = error.getDefaultMessage(); + errors.put(fieldName, errorMessage); + }); + + Map response = new HashMap<>(); + response.put("status", HttpStatus.BAD_REQUEST.value()); + response.put("errors", errors); + response.put("timestamp", LocalDateTime.now()); + + return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(response); + } + + @ExceptionHandler(Exception.class) + public ResponseEntity handleGenericException(Exception ex) { + ErrorResponse error = new ErrorResponse( + HttpStatus.INTERNAL_SERVER_ERROR.value(), + "An unexpected error occurred: " + ex.getMessage(), + LocalDateTime.now() + ); + return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(error); + } +} + +record ErrorResponse(int status, String message, LocalDateTime timestamp) {} diff --git a/backend/src/main/java/com/petshop/backend/exception/ResourceNotFoundException.java b/backend/src/main/java/com/petshop/backend/exception/ResourceNotFoundException.java new file mode 100644 index 00000000..08b426b7 --- /dev/null +++ b/backend/src/main/java/com/petshop/backend/exception/ResourceNotFoundException.java @@ -0,0 +1,7 @@ +package com.petshop.backend.exception; + +public class ResourceNotFoundException extends RuntimeException { + public ResourceNotFoundException(String message) { + super(message); + } +} diff --git a/backend/src/main/java/com/petshop/backend/service/AdoptionService.java b/backend/src/main/java/com/petshop/backend/service/AdoptionService.java new file mode 100644 index 00000000..f55cf358 --- /dev/null +++ b/backend/src/main/java/com/petshop/backend/service/AdoptionService.java @@ -0,0 +1,104 @@ +package com.petshop.backend.service; + +import com.petshop.backend.dto.adoption.AdoptionRequest; +import com.petshop.backend.dto.adoption.AdoptionResponse; +import com.petshop.backend.dto.common.BulkDeleteRequest; +import com.petshop.backend.entity.Adoption; +import com.petshop.backend.entity.Customer; +import com.petshop.backend.entity.Pet; +import com.petshop.backend.exception.ResourceNotFoundException; +import com.petshop.backend.repository.AdoptionRepository; +import com.petshop.backend.repository.CustomerRepository; +import com.petshop.backend.repository.PetRepository; +import lombok.RequiredArgsConstructor; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.Pageable; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +@Service +@RequiredArgsConstructor +public class AdoptionService { + + private final AdoptionRepository adoptionRepository; + private final PetRepository petRepository; + private final CustomerRepository customerRepository; + + public Page getAllAdoptions(Pageable pageable) { + return adoptionRepository.findAll(pageable).map(this::mapToResponse); + } + + public AdoptionResponse getAdoptionById(Long id) { + Adoption adoption = adoptionRepository.findById(id) + .orElseThrow(() -> new ResourceNotFoundException("Adoption not found with id: " + id)); + return mapToResponse(adoption); + } + + @Transactional + public AdoptionResponse createAdoption(AdoptionRequest request) { + Pet pet = petRepository.findById(request.getPetId()) + .orElseThrow(() -> new ResourceNotFoundException("Pet not found with id: " + request.getPetId())); + + Customer customer = customerRepository.findById(request.getCustomerId()) + .orElseThrow(() -> new ResourceNotFoundException("Customer not found with id: " + request.getCustomerId())); + + Adoption adoption = new Adoption(); + adoption.setPet(pet); + adoption.setCustomer(customer); + adoption.setAdoptionDate(request.getAdoptionDate()); + adoption.setAdoptionFee(request.getAdoptionFee()); + adoption.setNotes(request.getNotes()); + + adoption = adoptionRepository.save(adoption); + return mapToResponse(adoption); + } + + @Transactional + public AdoptionResponse updateAdoption(Long id, AdoptionRequest request) { + Adoption adoption = adoptionRepository.findById(id) + .orElseThrow(() -> new ResourceNotFoundException("Adoption not found with id: " + id)); + + Pet pet = petRepository.findById(request.getPetId()) + .orElseThrow(() -> new ResourceNotFoundException("Pet not found with id: " + request.getPetId())); + + Customer customer = customerRepository.findById(request.getCustomerId()) + .orElseThrow(() -> new ResourceNotFoundException("Customer not found with id: " + request.getCustomerId())); + + adoption.setPet(pet); + adoption.setCustomer(customer); + adoption.setAdoptionDate(request.getAdoptionDate()); + adoption.setAdoptionFee(request.getAdoptionFee()); + adoption.setNotes(request.getNotes()); + + adoption = adoptionRepository.save(adoption); + return mapToResponse(adoption); + } + + @Transactional + public void deleteAdoption(Long id) { + if (!adoptionRepository.existsById(id)) { + throw new ResourceNotFoundException("Adoption not found with id: " + id); + } + adoptionRepository.deleteById(id); + } + + @Transactional + public void bulkDeleteAdoptions(BulkDeleteRequest request) { + adoptionRepository.deleteAllById(request.getIds()); + } + + private AdoptionResponse mapToResponse(Adoption adoption) { + return new AdoptionResponse( + adoption.getId(), + adoption.getPet().getId(), + adoption.getPet().getPetName(), + adoption.getCustomer().getId(), + adoption.getCustomer().getCustomerName(), + adoption.getAdoptionDate(), + adoption.getAdoptionFee(), + adoption.getNotes(), + adoption.getCreatedAt(), + adoption.getUpdatedAt() + ); + } +} diff --git a/backend/src/main/java/com/petshop/backend/service/AppointmentService.java b/backend/src/main/java/com/petshop/backend/service/AppointmentService.java new file mode 100644 index 00000000..3c941b68 --- /dev/null +++ b/backend/src/main/java/com/petshop/backend/service/AppointmentService.java @@ -0,0 +1,140 @@ +package com.petshop.backend.service; + +import com.petshop.backend.dto.appointment.AppointmentRequest; +import com.petshop.backend.dto.appointment.AppointmentResponse; +import com.petshop.backend.dto.common.BulkDeleteRequest; +import com.petshop.backend.entity.Appointment; +import com.petshop.backend.entity.Customer; +import com.petshop.backend.entity.Pet; +import com.petshop.backend.exception.ResourceNotFoundException; +import com.petshop.backend.repository.AppointmentRepository; +import com.petshop.backend.repository.CustomerRepository; +import com.petshop.backend.repository.PetRepository; +import com.petshop.backend.repository.ServiceRepository; +import lombok.RequiredArgsConstructor; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.Pageable; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.util.HashSet; +import java.util.List; +import java.util.Set; +import java.util.stream.Collectors; + +@Service +@RequiredArgsConstructor +public class AppointmentService { + + private final AppointmentRepository appointmentRepository; + private final CustomerRepository customerRepository; + private final ServiceRepository serviceRepository; + private final PetRepository petRepository; + + public Page getAllAppointments(Pageable pageable) { + return appointmentRepository.findAll(pageable).map(this::mapToResponse); + } + + public AppointmentResponse getAppointmentById(Long id) { + Appointment appointment = appointmentRepository.findById(id) + .orElseThrow(() -> new ResourceNotFoundException("Appointment not found with id: " + id)); + return mapToResponse(appointment); + } + + @Transactional + public AppointmentResponse createAppointment(AppointmentRequest request) { + Customer customer = customerRepository.findById(request.getCustomerId()) + .orElseThrow(() -> new ResourceNotFoundException("Customer not found with id: " + request.getCustomerId())); + + com.petshop.backend.entity.Service service = serviceRepository.findById(request.getServiceId()) + .orElseThrow(() -> new ResourceNotFoundException("Service not found with id: " + request.getServiceId())); + + Set pets = fetchPets(request.getPetIds()); + + Appointment appointment = new Appointment(); + appointment.setCustomer(customer); + appointment.setService(service); + appointment.setAppointmentDate(request.getAppointmentDate()); + appointment.setAppointmentTime(request.getAppointmentTime()); + appointment.setStatus(request.getStatus()); + appointment.setPets(pets); + appointment.setNotes(request.getNotes()); + + appointment = appointmentRepository.save(appointment); + return mapToResponse(appointment); + } + + @Transactional + public AppointmentResponse updateAppointment(Long id, AppointmentRequest request) { + Appointment appointment = appointmentRepository.findById(id) + .orElseThrow(() -> new ResourceNotFoundException("Appointment not found with id: " + id)); + + Customer customer = customerRepository.findById(request.getCustomerId()) + .orElseThrow(() -> new ResourceNotFoundException("Customer not found with id: " + request.getCustomerId())); + + com.petshop.backend.entity.Service service = serviceRepository.findById(request.getServiceId()) + .orElseThrow(() -> new ResourceNotFoundException("Service not found with id: " + request.getServiceId())); + + Set pets = fetchPets(request.getPetIds()); + + appointment.setCustomer(customer); + appointment.setService(service); + appointment.setAppointmentDate(request.getAppointmentDate()); + appointment.setAppointmentTime(request.getAppointmentTime()); + appointment.setStatus(request.getStatus()); + appointment.setPets(pets); + appointment.setNotes(request.getNotes()); + + appointment = appointmentRepository.save(appointment); + return mapToResponse(appointment); + } + + @Transactional + public void deleteAppointment(Long id) { + if (!appointmentRepository.existsById(id)) { + throw new ResourceNotFoundException("Appointment not found with id: " + id); + } + appointmentRepository.deleteById(id); + } + + @Transactional + public void bulkDeleteAppointments(BulkDeleteRequest request) { + appointmentRepository.deleteAllById(request.getIds()); + } + + private Set fetchPets(List petIds) { + Set pets = new HashSet<>(); + for (Long petId : petIds) { + Pet pet = petRepository.findById(petId) + .orElseThrow(() -> new ResourceNotFoundException("Pet not found with id: " + petId)); + pets.add(pet); + } + return pets; + } + + private AppointmentResponse mapToResponse(Appointment appointment) { + List petNames = appointment.getPets().stream() + .map(Pet::getPetName) + .collect(Collectors.toList()); + + List petIds = appointment.getPets().stream() + .map(Pet::getId) + .collect(Collectors.toList()); + + return new AppointmentResponse( + appointment.getId(), + appointment.getCustomer().getId(), + appointment.getCustomer().getCustomerName(), + appointment.getService().getId(), + appointment.getService().getServiceName(), + appointment.getAppointmentDate(), + appointment.getAppointmentTime(), + appointment.getStatus() != null ? appointment.getStatus().toString() : null, + petNames, + petIds, + appointment.getNotes(), + appointment.getCreatedAt(), + appointment.getUpdatedAt() + ); + } +} diff --git a/backend/src/main/java/com/petshop/backend/service/CategoryService.java b/backend/src/main/java/com/petshop/backend/service/CategoryService.java new file mode 100644 index 00000000..7d596ce7 --- /dev/null +++ b/backend/src/main/java/com/petshop/backend/service/CategoryService.java @@ -0,0 +1,81 @@ +package com.petshop.backend.service; + +import com.petshop.backend.dto.category.CategoryRequest; +import com.petshop.backend.dto.category.CategoryResponse; +import com.petshop.backend.dto.common.BulkDeleteRequest; +import com.petshop.backend.entity.Category; +import com.petshop.backend.exception.ResourceNotFoundException; +import com.petshop.backend.repository.CategoryRepository; +import lombok.RequiredArgsConstructor; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.Pageable; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +@Service +@RequiredArgsConstructor +public class CategoryService { + + private final CategoryRepository categoryRepository; + + public Page getAllCategories(String query, Pageable pageable) { + Page categories; + if (query != null && !query.trim().isEmpty()) { + categories = categoryRepository.searchCategories(query, pageable); + } else { + categories = categoryRepository.findAll(pageable); + } + return categories.map(this::mapToResponse); + } + + public CategoryResponse getCategoryById(Long id) { + Category category = categoryRepository.findById(id) + .orElseThrow(() -> new ResourceNotFoundException("Category not found with id: " + id)); + return mapToResponse(category); + } + + @Transactional + public CategoryResponse createCategory(CategoryRequest request) { + Category category = new Category(); + category.setCategoryName(request.getCategoryName()); + category.setCategoryDescription(request.getCategoryDescription()); + + category = categoryRepository.save(category); + return mapToResponse(category); + } + + @Transactional + public CategoryResponse updateCategory(Long id, CategoryRequest request) { + Category category = categoryRepository.findById(id) + .orElseThrow(() -> new ResourceNotFoundException("Category not found with id: " + id)); + + category.setCategoryName(request.getCategoryName()); + category.setCategoryDescription(request.getCategoryDescription()); + + category = categoryRepository.save(category); + return mapToResponse(category); + } + + @Transactional + public void deleteCategory(Long id) { + if (!categoryRepository.existsById(id)) { + throw new ResourceNotFoundException("Category not found with id: " + id); + } + categoryRepository.deleteById(id); + } + + @Transactional + public void bulkDeleteCategories(BulkDeleteRequest request) { + categoryRepository.deleteAllById(request.getIds()); + } + + private CategoryResponse mapToResponse(Category category) { + return new CategoryResponse( + category.getId(), + category.getCategoryName(), + category.getCategoryDescription(), + category.getCreatedAt(), + category.getUpdatedAt() + ); + } +} diff --git a/backend/src/main/java/com/petshop/backend/service/CustomerService.java b/backend/src/main/java/com/petshop/backend/service/CustomerService.java new file mode 100644 index 00000000..fca95ad8 --- /dev/null +++ b/backend/src/main/java/com/petshop/backend/service/CustomerService.java @@ -0,0 +1,87 @@ +package com.petshop.backend.service; + +import com.petshop.backend.dto.common.BulkDeleteRequest; +import com.petshop.backend.dto.customer.CustomerRequest; +import com.petshop.backend.dto.customer.CustomerResponse; +import com.petshop.backend.entity.Customer; +import com.petshop.backend.exception.ResourceNotFoundException; +import com.petshop.backend.repository.CustomerRepository; +import lombok.RequiredArgsConstructor; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.Pageable; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +@Service +@RequiredArgsConstructor +public class CustomerService { + + private final CustomerRepository customerRepository; + + public Page getAllCustomers(String query, Pageable pageable) { + Page customers; + if (query != null && !query.trim().isEmpty()) { + customers = customerRepository.searchCustomers(query, pageable); + } else { + customers = customerRepository.findAll(pageable); + } + return customers.map(this::mapToResponse); + } + + public CustomerResponse getCustomerById(Long id) { + Customer customer = customerRepository.findById(id) + .orElseThrow(() -> new ResourceNotFoundException("Customer not found with id: " + id)); + return mapToResponse(customer); + } + + @Transactional + public CustomerResponse createCustomer(CustomerRequest request) { + Customer customer = new Customer(); + customer.setCustomerName(request.getCustomerName()); + customer.setCustomerEmail(request.getCustomerEmail()); + customer.setCustomerPhone(request.getCustomerPhone()); + customer.setCustomerAddress(request.getCustomerAddress()); + + customer = customerRepository.save(customer); + return mapToResponse(customer); + } + + @Transactional + public CustomerResponse updateCustomer(Long id, CustomerRequest request) { + Customer customer = customerRepository.findById(id) + .orElseThrow(() -> new ResourceNotFoundException("Customer not found with id: " + id)); + + customer.setCustomerName(request.getCustomerName()); + customer.setCustomerEmail(request.getCustomerEmail()); + customer.setCustomerPhone(request.getCustomerPhone()); + customer.setCustomerAddress(request.getCustomerAddress()); + + customer = customerRepository.save(customer); + return mapToResponse(customer); + } + + @Transactional + public void deleteCustomer(Long id) { + if (!customerRepository.existsById(id)) { + throw new ResourceNotFoundException("Customer not found with id: " + id); + } + customerRepository.deleteById(id); + } + + @Transactional + public void bulkDeleteCustomers(BulkDeleteRequest request) { + customerRepository.deleteAllById(request.getIds()); + } + + private CustomerResponse mapToResponse(Customer customer) { + return new CustomerResponse( + customer.getId(), + customer.getCustomerName(), + customer.getCustomerEmail(), + customer.getCustomerPhone(), + customer.getCustomerAddress(), + customer.getCreatedAt(), + customer.getUpdatedAt() + ); + } +} diff --git a/backend/src/main/java/com/petshop/backend/service/PetService.java b/backend/src/main/java/com/petshop/backend/service/PetService.java new file mode 100644 index 00000000..9d87cde2 --- /dev/null +++ b/backend/src/main/java/com/petshop/backend/service/PetService.java @@ -0,0 +1,93 @@ +package com.petshop.backend.service; + +import com.petshop.backend.dto.common.BulkDeleteRequest; +import com.petshop.backend.dto.pet.PetRequest; +import com.petshop.backend.dto.pet.PetResponse; +import com.petshop.backend.entity.Pet; +import com.petshop.backend.exception.ResourceNotFoundException; +import com.petshop.backend.repository.PetRepository; +import lombok.RequiredArgsConstructor; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.Pageable; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +@Service +@RequiredArgsConstructor +public class PetService { + + private final PetRepository petRepository; + + public Page getAllPets(String query, Pageable pageable) { + Page pets; + if (query != null && !query.trim().isEmpty()) { + pets = petRepository.searchPets(query, pageable); + } else { + pets = petRepository.findAll(pageable); + } + return pets.map(this::mapToResponse); + } + + public PetResponse getPetById(Long id) { + Pet pet = petRepository.findById(id) + .orElseThrow(() -> new ResourceNotFoundException("Pet not found with id: " + id)); + return mapToResponse(pet); + } + + @Transactional + public PetResponse createPet(PetRequest request) { + Pet pet = new Pet(); + pet.setPetName(request.getPetName()); + pet.setPetSpecies(request.getPetSpecies()); + pet.setPetBreed(request.getPetBreed()); + pet.setPetAge(request.getPetAge()); + pet.setPetStatus(request.getPetStatus()); + pet.setPetPrice(request.getPetPrice()); + + pet = petRepository.save(pet); + return mapToResponse(pet); + } + + @Transactional + public PetResponse updatePet(Long id, PetRequest request) { + Pet pet = petRepository.findById(id) + .orElseThrow(() -> new ResourceNotFoundException("Pet not found with id: " + id)); + + pet.setPetName(request.getPetName()); + pet.setPetSpecies(request.getPetSpecies()); + pet.setPetBreed(request.getPetBreed()); + pet.setPetAge(request.getPetAge()); + pet.setPetStatus(request.getPetStatus()); + pet.setPetPrice(request.getPetPrice()); + + pet = petRepository.save(pet); + return mapToResponse(pet); + } + + @Transactional + public void deletePet(Long id) { + if (!petRepository.existsById(id)) { + throw new ResourceNotFoundException("Pet not found with id: " + id); + } + petRepository.deleteById(id); + } + + @Transactional + public void bulkDeletePets(BulkDeleteRequest request) { + petRepository.deleteAllById(request.getIds()); + } + + private PetResponse mapToResponse(Pet pet) { + return new PetResponse( + pet.getId(), + pet.getPetName(), + pet.getPetSpecies(), + pet.getPetBreed(), + pet.getPetAge(), + pet.getPetStatus() != null ? pet.getPetStatus().toString() : null, + pet.getPetPrice(), + pet.getCreatedAt(), + pet.getUpdatedAt() + ); + } +} diff --git a/backend/src/main/java/com/petshop/backend/service/ProductService.java b/backend/src/main/java/com/petshop/backend/service/ProductService.java new file mode 100644 index 00000000..150d676d --- /dev/null +++ b/backend/src/main/java/com/petshop/backend/service/ProductService.java @@ -0,0 +1,100 @@ +package com.petshop.backend.service; + +import com.petshop.backend.dto.common.BulkDeleteRequest; +import com.petshop.backend.dto.product.ProductRequest; +import com.petshop.backend.dto.product.ProductResponse; +import com.petshop.backend.entity.Category; +import com.petshop.backend.entity.Product; +import com.petshop.backend.exception.ResourceNotFoundException; +import com.petshop.backend.repository.CategoryRepository; +import com.petshop.backend.repository.ProductRepository; +import lombok.RequiredArgsConstructor; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.Pageable; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +@Service +@RequiredArgsConstructor +public class ProductService { + + private final ProductRepository productRepository; + private final CategoryRepository categoryRepository; + + public Page getAllProducts(String query, Pageable pageable) { + Page products; + if (query != null && !query.trim().isEmpty()) { + products = productRepository.searchProducts(query, pageable); + } else { + products = productRepository.findAll(pageable); + } + return products.map(this::mapToResponse); + } + + public ProductResponse getProductById(Long id) { + Product product = productRepository.findById(id) + .orElseThrow(() -> new ResourceNotFoundException("Product not found with id: " + id)); + return mapToResponse(product); + } + + @Transactional + public ProductResponse createProduct(ProductRequest request) { + Category category = categoryRepository.findById(request.getCategoryId()) + .orElseThrow(() -> new ResourceNotFoundException("Category not found with id: " + request.getCategoryId())); + + Product product = new Product(); + product.setProductName(request.getProductName()); + product.setCategory(category); + product.setProductDescription(request.getProductDescription()); + product.setProductPrice(request.getProductPrice()); + product.setActive(request.getActive() != null ? request.getActive() : true); + + product = productRepository.save(product); + return mapToResponse(product); + } + + @Transactional + public ProductResponse updateProduct(Long id, ProductRequest request) { + Product product = productRepository.findById(id) + .orElseThrow(() -> new ResourceNotFoundException("Product not found with id: " + id)); + + Category category = categoryRepository.findById(request.getCategoryId()) + .orElseThrow(() -> new ResourceNotFoundException("Category not found with id: " + request.getCategoryId())); + + product.setProductName(request.getProductName()); + product.setCategory(category); + product.setProductDescription(request.getProductDescription()); + product.setProductPrice(request.getProductPrice()); + product.setActive(request.getActive() != null ? request.getActive() : true); + + product = productRepository.save(product); + return mapToResponse(product); + } + + @Transactional + public void deleteProduct(Long id) { + if (!productRepository.existsById(id)) { + throw new ResourceNotFoundException("Product not found with id: " + id); + } + productRepository.deleteById(id); + } + + @Transactional + public void bulkDeleteProducts(BulkDeleteRequest request) { + productRepository.deleteAllById(request.getIds()); + } + + private ProductResponse mapToResponse(Product product) { + return new ProductResponse( + product.getId(), + product.getProductName(), + product.getCategory().getId(), + product.getCategory().getCategoryName(), + product.getProductDescription(), + product.getProductPrice(), + product.getActive(), + product.getCreatedAt(), + product.getUpdatedAt() + ); + } +} diff --git a/backend/src/main/java/com/petshop/backend/service/ServiceService.java b/backend/src/main/java/com/petshop/backend/service/ServiceService.java new file mode 100644 index 00000000..15faea62 --- /dev/null +++ b/backend/src/main/java/com/petshop/backend/service/ServiceService.java @@ -0,0 +1,89 @@ +package com.petshop.backend.service; + +import com.petshop.backend.dto.common.BulkDeleteRequest; +import com.petshop.backend.dto.service.ServiceRequest; +import com.petshop.backend.dto.service.ServiceResponse; +import com.petshop.backend.exception.ResourceNotFoundException; +import com.petshop.backend.repository.ServiceRepository; +import lombok.RequiredArgsConstructor; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.Pageable; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +@Service +@RequiredArgsConstructor +public class ServiceService { + + private final ServiceRepository serviceRepository; + + 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 ServiceResponse getServiceById(Long id) { + com.petshop.backend.entity.Service service = serviceRepository.findById(id) + .orElseThrow(() -> new ResourceNotFoundException("Service not found with id: " + id)); + return mapToResponse(service); + } + + @Transactional + public ServiceResponse createService(ServiceRequest request) { + com.petshop.backend.entity.Service service = new com.petshop.backend.entity.Service(); + service.setServiceName(request.getServiceName()); + service.setServiceDescription(request.getServiceDescription()); + service.setServicePrice(request.getServicePrice()); + service.setServiceDurationMinutes(request.getServiceDurationMinutes()); + service.setActive(request.getActive() != null ? request.getActive() : true); + + service = serviceRepository.save(service); + return mapToResponse(service); + } + + @Transactional + public ServiceResponse updateService(Long id, ServiceRequest request) { + com.petshop.backend.entity.Service service = serviceRepository.findById(id) + .orElseThrow(() -> new ResourceNotFoundException("Service not found with id: " + id)); + + service.setServiceName(request.getServiceName()); + service.setServiceDescription(request.getServiceDescription()); + service.setServicePrice(request.getServicePrice()); + service.setServiceDurationMinutes(request.getServiceDurationMinutes()); + service.setActive(request.getActive() != null ? request.getActive() : true); + + service = serviceRepository.save(service); + return mapToResponse(service); + } + + @Transactional + public void deleteService(Long id) { + if (!serviceRepository.existsById(id)) { + throw new ResourceNotFoundException("Service not found with id: " + id); + } + serviceRepository.deleteById(id); + } + + @Transactional + public void bulkDeleteServices(BulkDeleteRequest request) { + serviceRepository.deleteAllById(request.getIds()); + } + + private ServiceResponse mapToResponse(com.petshop.backend.entity.Service service) { + return new ServiceResponse( + service.getId(), + service.getServiceName(), + service.getServiceDescription(), + service.getServicePrice(), + service.getServiceDurationMinutes(), + service.getActive(), + service.getCreatedAt(), + service.getUpdatedAt() + ); + } +}