add my pets api
This commit is contained in:
@@ -0,0 +1,76 @@
|
|||||||
|
package com.petshop.backend.controller;
|
||||||
|
|
||||||
|
import com.petshop.backend.dto.pet.MyPetRequest;
|
||||||
|
import com.petshop.backend.dto.pet.MyPetResponse;
|
||||||
|
import com.petshop.backend.entity.User;
|
||||||
|
import com.petshop.backend.repository.UserRepository;
|
||||||
|
import com.petshop.backend.service.PetService;
|
||||||
|
import com.petshop.backend.util.AuthenticationHelper;
|
||||||
|
import jakarta.validation.Valid;
|
||||||
|
import org.springframework.http.ResponseEntity;
|
||||||
|
import org.springframework.security.access.prepost.PreAuthorize;
|
||||||
|
import org.springframework.web.bind.annotation.DeleteMapping;
|
||||||
|
import org.springframework.web.bind.annotation.GetMapping;
|
||||||
|
import org.springframework.web.bind.annotation.PathVariable;
|
||||||
|
import org.springframework.web.bind.annotation.PostMapping;
|
||||||
|
import org.springframework.web.bind.annotation.PutMapping;
|
||||||
|
import org.springframework.web.bind.annotation.RequestBody;
|
||||||
|
import org.springframework.web.bind.annotation.RequestMapping;
|
||||||
|
import org.springframework.web.bind.annotation.RequestParam;
|
||||||
|
import org.springframework.web.bind.annotation.RestController;
|
||||||
|
import org.springframework.web.multipart.MultipartFile;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
@RestController
|
||||||
|
@RequestMapping("/api/v1/my-pets")
|
||||||
|
@PreAuthorize("isAuthenticated()")
|
||||||
|
public class MyPetController {
|
||||||
|
|
||||||
|
private final PetService petService;
|
||||||
|
private final UserRepository userRepository;
|
||||||
|
|
||||||
|
public MyPetController(PetService petService, UserRepository userRepository) {
|
||||||
|
this.petService = petService;
|
||||||
|
this.userRepository = userRepository;
|
||||||
|
}
|
||||||
|
|
||||||
|
@GetMapping
|
||||||
|
public ResponseEntity<List<MyPetResponse>> getMyPets() {
|
||||||
|
return ResponseEntity.ok(petService.getMyPets(currentUserId()));
|
||||||
|
}
|
||||||
|
|
||||||
|
@PostMapping
|
||||||
|
public ResponseEntity<MyPetResponse> createMyPet(@Valid @RequestBody MyPetRequest request) {
|
||||||
|
return ResponseEntity.ok(petService.createMyPet(currentUserId(), request));
|
||||||
|
}
|
||||||
|
|
||||||
|
@PutMapping("/{id}")
|
||||||
|
public ResponseEntity<MyPetResponse> updateMyPet(@PathVariable Long id, @Valid @RequestBody MyPetRequest request) {
|
||||||
|
return ResponseEntity.ok(petService.updateMyPet(currentUserId(), id, request));
|
||||||
|
}
|
||||||
|
|
||||||
|
@DeleteMapping("/{id}")
|
||||||
|
public ResponseEntity<Void> deleteMyPet(@PathVariable Long id) {
|
||||||
|
petService.deleteMyPet(currentUserId(), id);
|
||||||
|
return ResponseEntity.noContent().build();
|
||||||
|
}
|
||||||
|
|
||||||
|
@PostMapping("/{id}/image")
|
||||||
|
public ResponseEntity<?> uploadMyPetImage(@PathVariable Long id, @RequestParam("image") MultipartFile image) {
|
||||||
|
try {
|
||||||
|
return ResponseEntity.ok(petService.uploadMyPetImage(currentUserId(), id, image));
|
||||||
|
} catch (IllegalArgumentException ex) {
|
||||||
|
return ResponseEntity.badRequest().body(Map.of("message", ex.getMessage()));
|
||||||
|
} catch (IOException ex) {
|
||||||
|
return ResponseEntity.badRequest().body(Map.of("message", "Failed to upload pet image: " + ex.getMessage()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private Long currentUserId() {
|
||||||
|
User user = AuthenticationHelper.getAuthenticatedUser(userRepository);
|
||||||
|
return user.getId();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,42 @@
|
|||||||
|
package com.petshop.backend.dto.pet;
|
||||||
|
|
||||||
|
import jakarta.validation.constraints.NotBlank;
|
||||||
|
import jakarta.validation.constraints.Size;
|
||||||
|
|
||||||
|
public class MyPetRequest {
|
||||||
|
|
||||||
|
@NotBlank(message = "Pet name is required")
|
||||||
|
@Size(max = 50, message = "Pet name must not exceed 50 characters")
|
||||||
|
private String petName;
|
||||||
|
|
||||||
|
@NotBlank(message = "Species is required")
|
||||||
|
@Size(max = 50, message = "Species must not exceed 50 characters")
|
||||||
|
private String species;
|
||||||
|
|
||||||
|
@Size(max = 50, message = "Breed must not exceed 50 characters")
|
||||||
|
private String breed;
|
||||||
|
|
||||||
|
public String getPetName() {
|
||||||
|
return petName;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setPetName(String petName) {
|
||||||
|
this.petName = petName;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getSpecies() {
|
||||||
|
return species;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setSpecies(String species) {
|
||||||
|
this.species = species;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getBreed() {
|
||||||
|
return breed;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setBreed(String breed) {
|
||||||
|
this.breed = breed;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,61 @@
|
|||||||
|
package com.petshop.backend.dto.pet;
|
||||||
|
|
||||||
|
public class MyPetResponse {
|
||||||
|
|
||||||
|
private Long customerPetId;
|
||||||
|
private String petName;
|
||||||
|
private String species;
|
||||||
|
private String breed;
|
||||||
|
private String imageUrl;
|
||||||
|
|
||||||
|
public MyPetResponse() {
|
||||||
|
}
|
||||||
|
|
||||||
|
public MyPetResponse(Long customerPetId, String petName, String species, String breed, String imageUrl) {
|
||||||
|
this.customerPetId = customerPetId;
|
||||||
|
this.petName = petName;
|
||||||
|
this.species = species;
|
||||||
|
this.breed = breed;
|
||||||
|
this.imageUrl = imageUrl;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Long getCustomerPetId() {
|
||||||
|
return customerPetId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setCustomerPetId(Long customerPetId) {
|
||||||
|
this.customerPetId = customerPetId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getPetName() {
|
||||||
|
return petName;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setPetName(String petName) {
|
||||||
|
this.petName = petName;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getSpecies() {
|
||||||
|
return species;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setSpecies(String species) {
|
||||||
|
this.species = species;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getBreed() {
|
||||||
|
return breed;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setBreed(String breed) {
|
||||||
|
this.breed = breed;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getImageUrl() {
|
||||||
|
return imageUrl;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setImageUrl(String imageUrl) {
|
||||||
|
this.imageUrl = imageUrl;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -9,11 +9,14 @@ import org.springframework.data.repository.query.Param;
|
|||||||
import org.springframework.stereotype.Repository;
|
import org.springframework.stereotype.Repository;
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.Optional;
|
||||||
|
|
||||||
@Repository
|
@Repository
|
||||||
public interface PetRepository extends JpaRepository<Pet, Long> {
|
public interface PetRepository extends JpaRepository<Pet, Long> {
|
||||||
|
|
||||||
List<Pet> findAllByPetStatusIgnoreCaseOrderByPetNameAsc(String petStatus);
|
List<Pet> findAllByPetStatusIgnoreCaseOrderByPetNameAsc(String petStatus);
|
||||||
|
List<Pet> findAllByOwner_IdOrderByPetNameAsc(Long ownerId);
|
||||||
|
Optional<Pet> findByIdAndOwner_Id(Long id, Long ownerId);
|
||||||
|
|
||||||
@Query("SELECT p FROM Pet p WHERE " +
|
@Query("SELECT p FROM Pet p WHERE " +
|
||||||
"(:q IS NULL OR LOWER(p.petName) LIKE LOWER(CONCAT('%', :q, '%')) OR LOWER(p.petSpecies) LIKE LOWER(CONCAT('%', :q, '%')) OR LOWER(COALESCE(p.petBreed, '')) LIKE LOWER(CONCAT('%', :q, '%'))) AND " +
|
"(:q IS NULL OR LOWER(p.petName) LIKE LOWER(CONCAT('%', :q, '%')) OR LOWER(p.petSpecies) LIKE LOWER(CONCAT('%', :q, '%')) OR LOWER(COALESCE(p.petBreed, '')) LIKE LOWER(CONCAT('%', :q, '%'))) AND " +
|
||||||
|
|||||||
@@ -1,6 +1,8 @@
|
|||||||
package com.petshop.backend.service;
|
package com.petshop.backend.service;
|
||||||
|
|
||||||
import com.petshop.backend.dto.common.BulkDeleteRequest;
|
import com.petshop.backend.dto.common.BulkDeleteRequest;
|
||||||
|
import com.petshop.backend.dto.pet.MyPetRequest;
|
||||||
|
import com.petshop.backend.dto.pet.MyPetResponse;
|
||||||
import com.petshop.backend.dto.pet.PetRequest;
|
import com.petshop.backend.dto.pet.PetRequest;
|
||||||
import com.petshop.backend.dto.pet.PetResponse;
|
import com.petshop.backend.dto.pet.PetResponse;
|
||||||
import com.petshop.backend.entity.Adoption;
|
import com.petshop.backend.entity.Adoption;
|
||||||
@@ -25,6 +27,7 @@ import org.springframework.transaction.annotation.Transactional;
|
|||||||
import org.springframework.web.multipart.MultipartFile;
|
import org.springframework.web.multipart.MultipartFile;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
import java.util.List;
|
||||||
import java.util.Locale;
|
import java.util.Locale;
|
||||||
|
|
||||||
@Service
|
@Service
|
||||||
@@ -82,6 +85,50 @@ public class PetService {
|
|||||||
return mapToResponse(pet);
|
return mapToResponse(pet);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Transactional(readOnly = true)
|
||||||
|
public List<MyPetResponse> getMyPets(Long ownerUserId) {
|
||||||
|
return petRepository.findAllByOwner_IdOrderByPetNameAsc(ownerUserId).stream()
|
||||||
|
.map(this::mapToMyPetResponse)
|
||||||
|
.toList();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Transactional
|
||||||
|
public MyPetResponse createMyPet(Long ownerUserId, MyPetRequest request) {
|
||||||
|
User owner = userRepository.findById(ownerUserId)
|
||||||
|
.orElseThrow(() -> new ResourceNotFoundException("Customer not found with id: " + ownerUserId));
|
||||||
|
Pet pet = new Pet();
|
||||||
|
pet.setOwner(owner);
|
||||||
|
pet.setStore(null);
|
||||||
|
pet.setPetStatus("Owned");
|
||||||
|
applyMyPetRequest(pet, request);
|
||||||
|
return mapToMyPetResponse(petRepository.save(pet));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Transactional
|
||||||
|
public MyPetResponse updateMyPet(Long ownerUserId, Long petId, MyPetRequest request) {
|
||||||
|
Pet pet = findOwnedPet(ownerUserId, petId);
|
||||||
|
pet.setPetStatus("Owned");
|
||||||
|
pet.setStore(null);
|
||||||
|
applyMyPetRequest(pet, request);
|
||||||
|
return mapToMyPetResponse(petRepository.save(pet));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Transactional
|
||||||
|
public void deleteMyPet(Long ownerUserId, Long petId) {
|
||||||
|
Pet pet = findOwnedPet(ownerUserId, petId);
|
||||||
|
deleteStoredImageIfPresent(pet.getImageUrl());
|
||||||
|
petRepository.delete(pet);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Transactional
|
||||||
|
public MyPetResponse uploadMyPetImage(Long ownerUserId, Long petId, MultipartFile file) throws IOException {
|
||||||
|
validateImageFile(file);
|
||||||
|
Pet pet = findOwnedPet(ownerUserId, petId);
|
||||||
|
deleteStoredImageIfPresent(pet.getImageUrl());
|
||||||
|
pet.setImageUrl(catalogImageStorageService.storePetImage(file));
|
||||||
|
return mapToMyPetResponse(petRepository.save(pet));
|
||||||
|
}
|
||||||
|
|
||||||
@Transactional
|
@Transactional
|
||||||
public PetResponse createPet(PetRequest request) {
|
public PetResponse createPet(PetRequest request) {
|
||||||
Pet pet = new Pet();
|
Pet pet = new Pet();
|
||||||
@@ -225,6 +272,11 @@ public class PetService {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private Pet findOwnedPet(Long ownerUserId, Long petId) {
|
||||||
|
return petRepository.findByIdAndOwner_Id(petId, ownerUserId)
|
||||||
|
.orElseThrow(() -> new ResourceNotFoundException("Pet not found with id: " + petId));
|
||||||
|
}
|
||||||
|
|
||||||
private void deleteStoredImageIfPresent(String storedImagePath) {
|
private void deleteStoredImageIfPresent(String storedImagePath) {
|
||||||
if (storedImagePath == null || storedImagePath.isBlank()) {
|
if (storedImagePath == null || storedImagePath.isBlank()) {
|
||||||
return;
|
return;
|
||||||
@@ -276,6 +328,32 @@ public class PetService {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private MyPetResponse mapToMyPetResponse(Pet pet) {
|
||||||
|
return new MyPetResponse(
|
||||||
|
pet.getPetId(),
|
||||||
|
pet.getPetName(),
|
||||||
|
pet.getPetSpecies(),
|
||||||
|
pet.getPetBreed(),
|
||||||
|
pet.getImageUrl() != null && !pet.getImageUrl().isBlank() ? "/api/v1/pets/" + pet.getPetId() + "/image" : null
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void applyMyPetRequest(Pet pet, MyPetRequest request) {
|
||||||
|
pet.setPetName(request.getPetName().trim());
|
||||||
|
pet.setPetSpecies(request.getSpecies().trim());
|
||||||
|
pet.setPetBreed(normalizeOptional(request.getBreed()));
|
||||||
|
pet.setPetAge(null);
|
||||||
|
pet.setPetPrice(null);
|
||||||
|
}
|
||||||
|
|
||||||
|
private String normalizeOptional(String value) {
|
||||||
|
if (value == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
String trimmed = value.trim();
|
||||||
|
return trimmed.isEmpty() ? null : trimmed;
|
||||||
|
}
|
||||||
|
|
||||||
private void applyOwnerAndStore(Pet pet, PetRequest request) {
|
private void applyOwnerAndStore(Pet pet, PetRequest request) {
|
||||||
if ("owned".equalsIgnoreCase(request.getPetStatus())) {
|
if ("owned".equalsIgnoreCase(request.getPetStatus())) {
|
||||||
if (request.getCustomerId() != null) {
|
if (request.getCustomerId() != null) {
|
||||||
|
|||||||
Reference in New Issue
Block a user