Skip to content

Commit

Permalink
feat: 보호소 입장에서 보호 동물 조회 & 검색 로직을 구현한다. (#143)
Browse files Browse the repository at this point in the history
* feat: 보호 동물 검색 필터에 맞는 Enum 값을 구현한다.

* feat: 보호소 입장에서의 보호 동물 조회 & 검색 쿼리를 구현한다.

* feat: 보호소 입장에서의 보호 동물 조회 & 검색 서비스 로직을 구현한다.

* feat: 보호소 입장에서의 보호 동물 조회 & 검색 컨트롤러를 구현한다.

* refactor: 보호소 입장에서의 보호 동물 조회 & 검색 쿼리 메서드의 null 값을 검사한다.

* refactor: 보호소 입장에서의 보호 동물 조회 & 검색 쿼리 메서드 중 나이 검색 로직을 변경한다.

* test: 보호소 입장에서의 보호 동물 조회 & 검색 레포지토리 테스트 코드를 작성한다.

* test: 보호소 입장에서의 보호 동물 조회 & 검색 서비스 테스트 코드를 작성한다.

* test: 보호소 입장에서의 보호 동물 조회 & 검색 컨트롤러 테스트 코드를 작성한다.

* test: 보호소 입장에서의 보호 동물 조회 & 검색 레포지토리 테스트 코드를 추가한다.

* fix: FindAnimalsByShelterRequest에서 불필요한 생성자를 제거한다.
  • Loading branch information
bjo6300 authored Nov 7, 2023
1 parent 810362a commit c356d89
Show file tree
Hide file tree
Showing 15 changed files with 663 additions and 4 deletions.
31 changes: 31 additions & 0 deletions src/main/java/com/clova/anifriends/domain/animal/AnimalAge.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
package com.clova.anifriends.domain.animal;

import com.clova.anifriends.domain.common.EnumType;

public enum AnimalAge implements EnumType {
BABY(0, 6),
JUNIOR(7, 35),
ADULT(36, 83),
SENIOR(84, Integer.MAX_VALUE);

private final int minMonth;
private final int maxMonth;

AnimalAge(int minMonth, int maxMonth) {
this.minMonth = minMonth;
this.maxMonth = maxMonth;
}

public int getMinMonth() {
return this.minMonth;
}

public int getMaxMonth() {
return this.maxMonth;
}

@Override
public String getName() {
return this.name();
}
}
31 changes: 31 additions & 0 deletions src/main/java/com/clova/anifriends/domain/animal/AnimalSize.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
package com.clova.anifriends.domain.animal;

import com.clova.anifriends.domain.common.EnumType;

public enum AnimalSize implements EnumType {
SMALL(0, 8),
MEDIUM(8, 18),
LARGE(18, Integer.MAX_VALUE),
;

private final int minWeight;
private final int maxWeight;

AnimalSize(int minWeight, int maxWeight) {
this.minWeight = minWeight;
this.maxWeight = maxWeight;
}

public int getMinWeight() {
return this.minWeight;
}

public int getMaxWeight() {
return this.maxWeight;
}

@Override
public String getName() {
return this.name();
}
}
Original file line number Diff line number Diff line change
@@ -1,16 +1,20 @@
package com.clova.anifriends.domain.animal.controller;

import com.clova.anifriends.domain.animal.dto.request.FindAnimalsByShelterRequest;
import com.clova.anifriends.domain.animal.dto.request.RegisterAnimalRequest;
import com.clova.anifriends.domain.animal.dto.response.FindAnimalByShelterResponse;
import com.clova.anifriends.domain.animal.dto.response.FindAnimalByVolunteerResponse;
import com.clova.anifriends.domain.animal.dto.response.FindAnimalsByShelterResponse;
import com.clova.anifriends.domain.animal.dto.response.RegisterAnimalResponse;
import com.clova.anifriends.domain.animal.service.AnimalService;
import com.clova.anifriends.domain.auth.resolver.LoginUser;
import jakarta.validation.Valid;
import java.net.URI;
import lombok.RequiredArgsConstructor;
import org.springframework.data.domain.Pageable;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
Expand All @@ -24,7 +28,6 @@ public class AnimalController {

private final AnimalService animalService;


@PostMapping("/shelters/animals")
public ResponseEntity<Void> registerAnimal(
@LoginUser Long userId,
Expand All @@ -47,4 +50,23 @@ public ResponseEntity<FindAnimalByShelterResponse> findAnimalByShelter(
@PathVariable Long animalId) {
return ResponseEntity.ok(animalService.findAnimalByShelter(animalId, shelterId));
}

@GetMapping("/shelters/animals")
public ResponseEntity<FindAnimalsByShelterResponse> findAnimalsByShelter(
@LoginUser Long shelterId,
@ModelAttribute @Valid FindAnimalsByShelterRequest findAnimalsByShelterRequest,
Pageable pageable
) {
return ResponseEntity.ok(animalService.findAnimalsByShelter(
shelterId,
findAnimalsByShelterRequest.keyword(),
findAnimalsByShelterRequest.type(),
findAnimalsByShelterRequest.gender(),
findAnimalsByShelterRequest.isNeutered(),
findAnimalsByShelterRequest.active(),
findAnimalsByShelterRequest.size(),
findAnimalsByShelterRequest.age(),
pageable
));
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
package com.clova.anifriends.domain.animal.dto.request;

import com.clova.anifriends.domain.animal.AnimalAge;
import com.clova.anifriends.domain.animal.AnimalSize;
import com.clova.anifriends.domain.animal.wrapper.AnimalActive;
import com.clova.anifriends.domain.animal.wrapper.AnimalGender;
import com.clova.anifriends.domain.animal.wrapper.AnimalType;

public record FindAnimalsByShelterRequest(
String keyword,
AnimalType type,
AnimalGender gender,
Boolean isNeutered,
AnimalActive active,
AnimalSize size,
AnimalAge age
) {

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
package com.clova.anifriends.domain.animal.dto.response;

import com.clova.anifriends.domain.animal.Animal;
import com.clova.anifriends.domain.common.PageInfo;
import java.time.LocalDate;
import java.util.List;
import org.springframework.data.domain.Page;

public record FindAnimalsByShelterResponse(
List<FindAnimalByShelterResponse> animals,
PageInfo pageInfo
) {

public record FindAnimalByShelterResponse(
Long animalId,
String animalName,
String animalImageUrl,
LocalDate animalBirthDate,
String animalGender,
boolean animalIsAdopted,
boolean animalIsNeutered
) {

public static FindAnimalByShelterResponse from(
Animal animal
) {
return new FindAnimalByShelterResponse(
animal.getAnimalId(),
animal.getName(),
animal.getImageUrls().get(0),
animal.getBirthDate(),
animal.getGender().getName(),
animal.isAdopted(),
animal.isNeutered()
);
}
}

public static FindAnimalsByShelterResponse from(
Page<Animal> animals
) {
PageInfo pageInfo = PageInfo.of(animals.getTotalElements(), animals.hasNext());
List<FindAnimalByShelterResponse> content = animals.getContent()
.stream()
.map(FindAnimalByShelterResponse::from)
.toList();

return new FindAnimalsByShelterResponse(content, pageInfo);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.query.Param;

public interface AnimalRepository extends JpaRepository<Animal, Long> {
public interface AnimalRepository extends JpaRepository<Animal, Long>, AnimalRepositoryCustom {

@Query("select a from Animal a"
+ " join fetch a.images"
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
package com.clova.anifriends.domain.animal.repository;

import com.clova.anifriends.domain.animal.Animal;
import com.clova.anifriends.domain.animal.AnimalAge;
import com.clova.anifriends.domain.animal.AnimalSize;
import com.clova.anifriends.domain.animal.wrapper.AnimalActive;
import com.clova.anifriends.domain.animal.wrapper.AnimalGender;
import com.clova.anifriends.domain.animal.wrapper.AnimalType;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;

public interface AnimalRepositoryCustom {

Page<Animal> findAnimalsByShelter(
Long shelterId,
String keyword,
AnimalType type,
AnimalGender gender,
Boolean isNeutered,
AnimalActive active,
AnimalSize size,
AnimalAge age,
Pageable pageable
);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,130 @@
package com.clova.anifriends.domain.animal.repository;

import static com.clova.anifriends.domain.animal.QAnimal.animal;

import com.clova.anifriends.domain.animal.Animal;
import com.clova.anifriends.domain.animal.AnimalAge;
import com.clova.anifriends.domain.animal.AnimalSize;
import com.clova.anifriends.domain.animal.wrapper.AnimalActive;
import com.clova.anifriends.domain.animal.wrapper.AnimalGender;
import com.clova.anifriends.domain.animal.wrapper.AnimalType;
import com.querydsl.core.types.dsl.BooleanExpression;
import com.querydsl.jpa.impl.JPAQueryFactory;
import java.time.LocalDate;
import java.util.List;
import java.util.Objects;
import lombok.RequiredArgsConstructor;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageImpl;
import org.springframework.data.domain.Pageable;
import org.springframework.stereotype.Repository;

@Repository
@RequiredArgsConstructor
public class AnimalRepositoryImpl implements AnimalRepositoryCustom {

private final JPAQueryFactory query;

@Override
public Page<Animal> findAnimalsByShelter(
Long shelterId,
String keyword,
AnimalType type,
AnimalGender gender,
Boolean isNeutered,
AnimalActive active,
AnimalSize size,
AnimalAge age,
Pageable pageable
) {
List<Animal> animals = query.select(animal)
.from(animal)
.where(
animal.shelter.shelterId.eq(shelterId),
animalNameContains(keyword),
animalTypeContains(type),
animalGenderContains(gender),
animalIsNeutered(isNeutered),
animalActiveContains(active),
animalSizeContains(size),
animalAgeContains(age)
)
.limit(pageable.getPageSize())
.offset(pageable.getOffset())
.fetch();

Long count = query.select(animal.count())
.from(animal)
.where(
animal.shelter.shelterId.eq(shelterId),
animalNameContains(keyword),
animalTypeContains(type),
animalGenderContains(gender),
animalIsNeutered(isNeutered),
animalActiveContains(active),
animalSizeContains(size),
animalAgeContains(age)
)
.fetchOne();

return new PageImpl<>(animals, pageable, count);
}

private BooleanExpression animalNameContains(String keyword) {
return keyword != null ? animal.name.name.contains(keyword) : null;
}

private BooleanExpression animalTypeContains(
AnimalType type
) {
return type != null ? animal.type.stringValue().eq(type.getName()) : null;
}

private BooleanExpression animalGenderContains(
AnimalGender gender
) {
return gender != null ? animal.gender.stringValue().eq(gender.getName()) : null;
}

private BooleanExpression animalIsNeutered(
Boolean isNeuteredFilter
) {
return isNeuteredFilter != null ? animal.neutered.isNeutered.eq(isNeuteredFilter) : null;
}

private BooleanExpression animalActiveContains(
AnimalActive active
) {
return active != null ? animal.active.stringValue().contains(active.getName()) : null;
}

private BooleanExpression animalSizeContains(
AnimalSize size
) {
if (Objects.isNull(size)) {
return null;
}

int minWeight = size.getMinWeight();
int maxWeight = size.getMaxWeight();

return animal.weight.weight.between(minWeight, maxWeight);
}

private BooleanExpression animalAgeContains(
AnimalAge age
) {
if (Objects.isNull(age)) {
return null;
}

int minMonth = age.getMinMonth();
int maxMonth = age.getMaxMonth();

LocalDate currentDate = LocalDate.now();
LocalDate minDate = currentDate.minusMonths(minMonth);
LocalDate maxDate = currentDate.minusMonths(maxMonth);

return animal.birthDate.between(maxDate, minDate);
}
}
Loading

0 comments on commit c356d89

Please sign in to comment.