AdoptionService.java
package org.petify.shelter.service;
import org.petify.shelter.client.AchievementClient;
import org.petify.shelter.dto.AdoptionRequest;
import org.petify.shelter.dto.AdoptionResponse;
import org.petify.shelter.enums.AdoptionStatus;
import org.petify.shelter.exception.AdoptionAlreadyExistsException;
import org.petify.shelter.exception.AdoptionFormNotFoundException;
import org.petify.shelter.exception.PetIsArchivedException;
import org.petify.shelter.exception.PetNotFoundException;
import org.petify.shelter.exception.ShelterNotFoundException;
import org.petify.shelter.mapper.AdoptionMapper;
import org.petify.shelter.model.Adoption;
import org.petify.shelter.repository.AdoptionRepository;
import org.petify.shelter.repository.PetRepository;
import org.petify.shelter.repository.ShelterRepository;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.security.access.AccessDeniedException;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.util.List;
import java.util.stream.Collectors;
@AllArgsConstructor
@Service
@Slf4j
public class AdoptionService {
private final AdoptionRepository adoptionRepository;
private final ShelterRepository shelterRepository;
private final PetRepository petRepository;
private final AdoptionMapper adoptionMapper;
private final AchievementClient achievementClient;
@Transactional
public AdoptionResponse createAdoptionForm(Long petId, String username, AdoptionRequest adoptionRequest) {
var pet = petRepository.findById(petId)
.orElseThrow(() -> new PetNotFoundException(petId));
if (pet.isArchived()) {
throw new PetIsArchivedException(petId);
}
if (adoptionRepository.existsByPetIdAndUsername(petId, username)) {
throw new AdoptionAlreadyExistsException(petId, username);
}
var adoption = adoptionMapper.toEntity(adoptionRequest);
adoption.setUsername(username);
adoption.setPet(pet);
var savedForm = adoptionRepository.save(adoption);
return adoptionMapper.toDto(savedForm);
}
public List<AdoptionResponse> getUserAdoptionForms(String username) {
var adoptions = adoptionRepository.findByUsername(username);
return adoptions.stream()
.map(adoptionMapper::toDto)
.collect(Collectors.toList());
}
public List<AdoptionResponse> getShelterAdoptionForms(Long shelterId) {
var shelter = shelterRepository.findById(shelterId)
.orElseThrow(() -> new ShelterNotFoundException(shelterId));
var adoptions = adoptionRepository.findByPetShelter(shelter);
return adoptions.stream()
.map(adoptionMapper::toDto)
.collect(Collectors.toList());
}
public List<AdoptionResponse> getPetAdoptionForms(Long petId) {
var pet = petRepository.findById(petId)
.orElseThrow(() -> new PetNotFoundException(petId));
var adoptions = adoptionRepository.findByPet(pet);
return adoptions.stream()
.map(adoptionMapper::toDto)
.collect(Collectors.toList());
}
@Transactional
public AdoptionResponse updateAdoptionStatus(Long formId, AdoptionStatus newStatus, String username) {
var form = adoptionRepository.findById(formId)
.orElseThrow(() -> new AdoptionFormNotFoundException(formId));
if (!form.getPet().getShelter().getOwnerUsername().equals(username)) {
throw new AccessDeniedException("You are not allowed to update this adoption form");
}
if (newStatus == AdoptionStatus.ACCEPTED) {
var otherPendingForms = adoptionRepository
.findByPetAndAdoptionStatusAndIdNot(form.getPet(), AdoptionStatus.PENDING, formId);
for (Adoption otherForm : otherPendingForms) {
otherForm.setAdoptionStatus(AdoptionStatus.REJECTED);
adoptionRepository.save(otherForm);
}
var pet = form.getPet();
pet.setArchived(true);
petRepository.save(pet);
try {
achievementClient.trackAdoptionProgressForUser(form.getUsername());
log.info("Tracked adoption achievement for user: {}", form.getUsername());
} catch (Exception e) {
log.error("Failed to track adoption achievement for user {}: {}",
form.getUsername(), e.getMessage());
}
}
form.setAdoptionStatus(newStatus);
var updatedForm = adoptionRepository.save(form);
return adoptionMapper.toDto(updatedForm);
}
@Transactional
public AdoptionResponse cancelAdoptionForm(Long formId, String username) {
var form = adoptionRepository.findById(formId)
.orElseThrow(() -> new AdoptionFormNotFoundException(formId));
if (!form.getUsername().equals(username)) {
throw new AccessDeniedException("You can only cancel your own adoption forms.");
}
if (form.getAdoptionStatus() != AdoptionStatus.PENDING) {
throw new IllegalStateException("Only pending adoption forms can be cancelled");
}
form.setAdoptionStatus(AdoptionStatus.CANCELLED);
var updatedForm = adoptionRepository.save(form);
return adoptionMapper.toDto(updatedForm);
}
@Transactional
public void deleteAdoptionForm(Long formId, String username) {
Adoption form = adoptionRepository.findById(formId)
.orElseThrow(() -> new AdoptionFormNotFoundException(formId));
if (!form.getPet().getShelter().getOwnerUsername().equals(username)) {
throw new AccessDeniedException("You are not allowed to delete this adoption form.");
}
if (form.getAdoptionStatus() == AdoptionStatus.PENDING || form.getAdoptionStatus() == AdoptionStatus.ACCEPTED) {
throw new IllegalStateException("You cannot delete a pending or accepted adoption form. Please reject or cancel it first.");
}
adoptionRepository.delete(form);
}
public AdoptionResponse getAdoptionFormById(Long formId) {
var form = adoptionRepository.findById(formId)
.orElseThrow(() -> new AdoptionFormNotFoundException(formId));
return adoptionMapper.toDto(form);
}
public List<AdoptionResponse> getAdoptionsByUsername(String username) {
List<Adoption> adoptions = adoptionRepository.findByUsername(username);
return adoptions.stream()
.map(adoptionMapper::toDto)
.collect(Collectors.toList());
}
}