diff --git a/build.gradle b/build.gradle index 2be4047..97c68d1 100644 --- a/build.gradle +++ b/build.gradle @@ -29,6 +29,12 @@ dependencies { implementation 'org.springframework.boot:spring-boot-starter-validation' //implementation 'org.vaadin.android:android-json:0.0.20131108.vaadin1' + //querydsl + implementation 'com.querydsl:querydsl-jpa:5.0.0:jakarta' + annotationProcessor "com.querydsl:querydsl-apt:${dependencyManagement.importedProperties['querydsl.version']}:jakarta" + annotationProcessor "jakarta.annotation:jakarta.annotation-api" + annotationProcessor "jakarta.persistence:jakarta.persistence-api" + implementation 'org.json:json:20210307' @@ -61,6 +67,27 @@ dependencies { runtimeOnly ("io.micrometer:micrometer-registry-prometheus") } +def querydslSrcDir = 'src/main/generated' +sourceSets { + main { + java { + srcDirs += [ querydslSrcDir ] + } + } +} + +compileJava { + options.compilerArgs << '-Aquerydsl.generatedAnnotationClass=javax.annotation.Generated' +} + +tasks.withType(JavaCompile) { + options.generatedSourceOutputDirectory = file(querydslSrcDir) +} + tasks.named('test') { useJUnitPlatform() } + +clean { + delete file('src/main/generated') +} diff --git a/src/main/java/com/techeer/abandoneddog/AbandoneddogApplication.java b/src/main/java/com/techeer/abandoneddog/AbandoneddogApplication.java index 2b20e07..d038127 100644 --- a/src/main/java/com/techeer/abandoneddog/AbandoneddogApplication.java +++ b/src/main/java/com/techeer/abandoneddog/AbandoneddogApplication.java @@ -2,10 +2,12 @@ import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.data.jpa.repository.config.EnableJpaRepositories; import org.springframework.scheduling.annotation.EnableScheduling; @SpringBootApplication @EnableScheduling +@EnableJpaRepositories public class AbandoneddogApplication { public static void main(String[] args) { diff --git a/src/main/java/com/techeer/abandoneddog/global/config/QuerydslConfig.java b/src/main/java/com/techeer/abandoneddog/global/config/QuerydslConfig.java new file mode 100644 index 0000000..3f67c92 --- /dev/null +++ b/src/main/java/com/techeer/abandoneddog/global/config/QuerydslConfig.java @@ -0,0 +1,18 @@ +package com.techeer.abandoneddog.global.config; + +import com.querydsl.jpa.impl.JPAQueryFactory; +import jakarta.persistence.EntityManager; +import jakarta.persistence.PersistenceContext; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +@Configuration +public class QuerydslConfig { + @PersistenceContext + private EntityManager entityManager; + + @Bean + public JPAQueryFactory jpaQueryFactory() { + return new JPAQueryFactory(entityManager); + } +} \ No newline at end of file diff --git a/src/main/java/com/techeer/abandoneddog/pet_board/controller/PetBoardController.java b/src/main/java/com/techeer/abandoneddog/pet_board/controller/PetBoardController.java index 03352c3..4ef592b 100644 --- a/src/main/java/com/techeer/abandoneddog/pet_board/controller/PetBoardController.java +++ b/src/main/java/com/techeer/abandoneddog/pet_board/controller/PetBoardController.java @@ -26,6 +26,7 @@ import java.util.LinkedHashMap; import java.util.List; import java.util.Map; +import java.util.stream.Collectors; @RestController @@ -47,13 +48,32 @@ public PetBoardController(PetBoardService petBoardService) { @ApiResponse(responseCode = "400", description = "게시물 작성 실패") }) public ResponseEntity createPetBoard( - @RequestPart(name = "petBoardRequestDto") @Parameter(description = "게시물 정보", content = @Content(mediaType = MediaType.APPLICATION_JSON_VALUE)) PetBoardRequestDto petBoardRequestDto, - @RequestPart(name = "mainImage", required = true) @Parameter(description = "메인 이미지", content = @Content(mediaType = MediaType.MULTIPART_FORM_DATA_VALUE)) MultipartFile mainImage, - @RequestPart(name = "images", required = true) @Parameter(description = "추가 이미지 목록", content = @Content(mediaType = MediaType.MULTIPART_FORM_DATA_VALUE)) List images) { + @RequestPart(name ="petBoardRequestDto") PetBoardRequestDto petBoardRequestDto, + @RequestPart(name ="mainImage") MultipartFile mainImage, + @RequestPart(name ="images") List images) { try { + log.info("Received DTO: {}", petBoardRequestDto); + log.info("Received main image: {}", mainImage.getOriginalFilename()); + log.info("Received additional images: {}", images.stream().map(MultipartFile::getOriginalFilename).collect(Collectors.joining(", "))); + + // 요청 파라미터 검증 + if (petBoardRequestDto == null) { + throw new IllegalArgumentException("게시물 정보가 누락되었습니다."); + } + if (mainImage == null || mainImage.isEmpty()) { + throw new IllegalArgumentException("메인 이미지가 누락되었습니다."); + } + if (images == null || images.isEmpty()) { + throw new IllegalArgumentException("이미지 리스트가 누락되었습니다."); + } + long petBoardId = petBoardService.createPetBoard(petBoardRequestDto, mainImage, images); return ResponseEntity.ok().body("게시물 작성에 성공하였습니다."); + } catch (IllegalArgumentException e) { + log.error("입력 값 검증 실패: {}", e.getMessage()); + return ResponseEntity.status(HttpStatus.BAD_REQUEST).body("입력 값이 잘못되었습니다: " + e.getMessage()); } catch (Exception e) { + log.error("게시물 작성 중 예외 발생: {}", e.getMessage()); return ResponseEntity.status(HttpStatus.BAD_REQUEST).body("게시물 작성에 실패하였습니다."); } } @@ -80,7 +100,8 @@ public ResponseEntity updatePetBoard(@PathVariable("petBoardId") Long petBoar } @GetMapping("/{petBoardId}") - public ResponseEntity getPetBoard(@PathVariable("petBoardId") Long petBoardId, @RequestParam(defaultValue = "-1") Long userId) { + public ResponseEntity getPetBoard(@PathVariable("petBoardId") Long petBoardId, @RequestParam(name="userId",defaultValue = "-1") Long userId) { + try { PetBoardDetailResponseDto responseDto = petBoardService.getPetBoard(petBoardId, userId); return ResponseEntity.ok(responseDto); @@ -93,9 +114,10 @@ public ResponseEntity getPetBoard(@PathVariable("petBoardId") Long petBoardId @GetMapping("/list") public ResponseEntity getPetBoards( - @RequestParam(defaultValue = "0") int page, - @RequestParam(defaultValue = "12") int size, - @RequestParam(defaultValue = "desc") String direction) { + @RequestParam(value="page",defaultValue = "0") int page, + @RequestParam(value="size",defaultValue = "12") int size, + @RequestParam(value="direction",defaultValue = "asc") String direction) { + try { // Sort sort = direction.equalsIgnoreCase("desc") ? Sort.by("petBoardId").descending() : Sort.by("petBoardId").ascending(); Sort sort = direction.equalsIgnoreCase("desc") ? Sort.by("createdAt").descending() : Sort.by("createdAt").ascending(); @@ -115,12 +137,14 @@ public ResponseEntity getPetBoards( } } + @GetMapping("/list/type/{petType}") public ResponseEntity getPetBoardsByPetType( - @PathVariable String petType, - @RequestParam(defaultValue = "0") int page, - @RequestParam(defaultValue = "12") int size, - @RequestParam(defaultValue = "desc") String direction) { + @PathVariable("petType") String petType, + @RequestParam(value="page",defaultValue = "0") int page, + @RequestParam(value="size",defaultValue = "12") int size, + @RequestParam(value="direction",defaultValue = "asc") String direction) { + try { Sort sort = direction.equalsIgnoreCase("desc") ? Sort.by("petBoardId").descending() : Sort.by("petBoardId").ascending(); // Sort sort = direction.equalsIgnoreCase("desc") ? Sort.by("createdAt").descending() : Sort.by("createdAt").ascending(); diff --git a/src/main/java/com/techeer/abandoneddog/pet_board/repository/PetBoardRepository.java b/src/main/java/com/techeer/abandoneddog/pet_board/repository/PetBoardRepository.java index 774dd74..611071d 100644 --- a/src/main/java/com/techeer/abandoneddog/pet_board/repository/PetBoardRepository.java +++ b/src/main/java/com/techeer/abandoneddog/pet_board/repository/PetBoardRepository.java @@ -16,7 +16,8 @@ import java.util.List; @Repository -public interface PetBoardRepository extends JpaRepository { +public interface PetBoardRepository extends JpaRepository, PetBoardRepositoryCustom { + Page findByPetInfoPetTypeAndStatus(String petType, Status status, Pageable pageable); Page findPetBoardByUsersId(Long userId, Pageable pageable); @@ -25,19 +26,19 @@ public interface PetBoardRepository extends JpaRepository { - @Query("SELECT pb FROM PetBoard pb JOIN pb.petInfo pi WHERE " + - "(:categories IS NULL OR pi.kindCd IN :categories) AND " + - "(:status IS NULL OR pb.status = :status) AND " + - "(:minAge IS NULL OR pi.age >= :minAge) AND " + - "(:maxAge IS NULL OR pi.age <= :maxAge) AND " + - "(:isYoung IS NULL OR pi.isYoung =:isYoung) AND " + - "(:title IS NULL OR pb.title LIKE %:title%)") - Page searchPetBoards(@Param("categories") String categories, - @Param("status") Status status, - @Param("minAge") Integer minAge, - @Param("maxAge") Integer maxAge, - @Param("title") String title, - @Param("isYoung") boolean isYoung, - - Pageable pageable); +// @Query("SELECT pb FROM PetBoard pb JOIN pb.petInfo pi WHERE " + +// "(:categories IS NULL OR pi.kindCd IN :categories) AND " + +// "(:status IS NULL OR pb.status = :status) AND " + +// "(:minAge IS NULL OR pi.age >= :minAge) AND " + +// "(:maxAge IS NULL OR pi.age <= :maxAge) AND " + +// "(:isYoung IS NULL OR pi.isYoung =:isYoung) AND " + +// "(:title IS NULL OR pb.title LIKE %:title%)") +// Page searchPetBoards(@Param("categories") String categories, +// @Param("status") Status status, +// @Param("minAge") Integer minAge, +// @Param("maxAge") Integer maxAge, +// @Param("title") String title, +// @Param("isYoung") boolean isYoung, +// +// Pageable pageable); } diff --git a/src/main/java/com/techeer/abandoneddog/pet_board/repository/PetBoardRepositoryCustom.java b/src/main/java/com/techeer/abandoneddog/pet_board/repository/PetBoardRepositoryCustom.java new file mode 100644 index 0000000..acd5186 --- /dev/null +++ b/src/main/java/com/techeer/abandoneddog/pet_board/repository/PetBoardRepositoryCustom.java @@ -0,0 +1,9 @@ +package com.techeer.abandoneddog.pet_board.repository; + +import com.techeer.abandoneddog.pet_board.entity.PetBoard; +import com.techeer.abandoneddog.pet_board.entity.Status; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.Pageable; +public interface PetBoardRepositoryCustom { + Page searchPetBoards(String categories, Status status, Integer minAge, Integer maxAge, String title, Boolean isYoung, Pageable pageable); +} \ No newline at end of file diff --git a/src/main/java/com/techeer/abandoneddog/pet_board/repository/PetBoardRepositoryImpl.java b/src/main/java/com/techeer/abandoneddog/pet_board/repository/PetBoardRepositoryImpl.java new file mode 100644 index 0000000..d8ecf62 --- /dev/null +++ b/src/main/java/com/techeer/abandoneddog/pet_board/repository/PetBoardRepositoryImpl.java @@ -0,0 +1,65 @@ +package com.techeer.abandoneddog.pet_board.repository; + +import com.querydsl.core.BooleanBuilder; +import com.querydsl.jpa.impl.JPAQuery; +import com.techeer.abandoneddog.animal.entity.QPetInfo; +import com.techeer.abandoneddog.pet_board.entity.PetBoard; +import com.techeer.abandoneddog.pet_board.entity.QPetBoard; +import com.techeer.abandoneddog.pet_board.entity.Status; +import jakarta.persistence.EntityManager; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.PageImpl; +import org.springframework.data.domain.Pageable; +import org.springframework.stereotype.Repository; + +import java.util.List; +@Repository +public class PetBoardRepositoryImpl implements PetBoardRepositoryCustom { + + @Autowired + private EntityManager entityManager; + + @Override + public Page searchPetBoards(String categories, Status status, Integer minAge, Integer maxAge, String title, Boolean isYoung, Pageable pageable) { + QPetBoard petBoard = QPetBoard.petBoard; + QPetInfo petInfo = QPetInfo.petInfo; + + JPAQuery query = new JPAQuery<>(entityManager); + BooleanBuilder builder = new BooleanBuilder(); + + query.from(petBoard) + .join(petBoard.petInfo, petInfo); + + if (categories != null) { + builder.and(petInfo.kindCd.eq(categories)); + } + if (status != null) { + builder.and(petBoard.status.eq(status)); + } + if (minAge != null) { + builder.and(petInfo.age.goe(minAge)); + } + if (maxAge != null) { + builder.and(petInfo.age.loe(maxAge)); + } + if (isYoung != null) { + builder.and(petInfo.isYoung.eq(isYoung)); + } + if (title != null) { + builder.and(petBoard.title.containsIgnoreCase(title)); + } + + query.where(builder); + + long total = query.fetchCount(); + List results = query + .offset(pageable.getOffset()) + .limit(pageable.getPageSize()) + .fetch(); + + return new PageImpl<>(results, pageable, total); + } + + +} \ No newline at end of file diff --git a/src/main/java/com/techeer/abandoneddog/pet_board/service/PetBoardService.java b/src/main/java/com/techeer/abandoneddog/pet_board/service/PetBoardService.java index e61d3a8..beda01a 100644 --- a/src/main/java/com/techeer/abandoneddog/pet_board/service/PetBoardService.java +++ b/src/main/java/com/techeer/abandoneddog/pet_board/service/PetBoardService.java @@ -168,8 +168,14 @@ public Page getPetBoardsByPetType(String petType, Pageable //필터링으로 검색 - public Page searchPetBoards(String categories, Status status, int minYear, int maxYear, String title, boolean isYoung, int page, int size) { + public Page searchPetBoards(String categories, Status status, Integer minYear, Integer maxYear, String title, Boolean isYoung, int page, int size) { Pageable pageable = PageRequest.of(page, size); + if (minYear == null) { + minYear = 0; // 혹은 0과 같이 적절한 기본값 + } + if (maxYear == null) { + maxYear = 50; // 혹은 적절한 기본값 + } Page petBoards = petBoardRepository.searchPetBoards(categories, status, minYear, maxYear, title, isYoung, pageable); return petBoards.map(PetBoardResponseDto::fromEntity);