Skip to content

Commit

Permalink
Merge pull request #68 from SSUMC-7th/randy/#9
Browse files Browse the repository at this point in the history
[Randy] Chapter 9. API & Paging
  • Loading branch information
Jangwon0319 authored Nov 27, 2024
2 parents d82ec28 + 428d069 commit b58d79f
Show file tree
Hide file tree
Showing 10 changed files with 200 additions and 0 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,9 @@ public enum ErrorStatus implements BaseErrorCode {
// FoodCategory Error
FOOD_CATEGORY_NOT_FOUND(HttpStatus.NOT_FOUND, "FOOD_CATEGORY4001", "음식 카테고리가 없습니다."),

// Store Error
STORE_NOT_FOUND(HttpStatus.NOT_FOUND, "STORE_4001","가게가 없습니다."),

// 예시,,,
ARTICLE_NOT_FOUND(HttpStatus.NOT_FOUND, "ARTICLE4001", "게시글이 없습니다."),

Expand Down
35 changes: 35 additions & 0 deletions src/main/java/umc/spring/converter/StoreConverter.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
package umc.spring.converter;

import org.springframework.data.domain.Page;
import umc.spring.domain.Review;
import umc.spring.web.dto.StoreResponseDTO;

import java.util.List;
import java.util.stream.Collectors;

public class StoreConverter {

public static StoreResponseDTO.ReviewPreViewDTO reviewPreViewDTO(Review review){
return StoreResponseDTO.ReviewPreViewDTO.builder()
.ownerNickname(review.getMember().getName())
.score(review.getScore())
.body(review.getBody())
.createdAt(review.getCreatedAt().toLocalDate())
.build();
}


public static StoreResponseDTO.ReviewPreViewListDTO reviewPreViewListDTO(Page<Review> reviewList){
List<StoreResponseDTO.ReviewPreViewDTO> reviewPreViewDTOList = reviewList.stream()
.map(StoreConverter::reviewPreViewDTO).collect(Collectors.toList());

return StoreResponseDTO.ReviewPreViewListDTO.builder()
.isLast(reviewList.isLast())
.isFirst(reviewList.isFirst())
.totalPage(reviewList.getTotalPages())
.totalElements(reviewList.getTotalElements())
.listSize(reviewPreViewDTOList.size())
.reviewList(reviewPreViewDTOList)
.build();
}
}
Original file line number Diff line number Diff line change
@@ -1,7 +1,12 @@
package umc.spring.repository.ReviewRepository;

import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.jpa.repository.JpaRepository;
import umc.spring.domain.Review;
import umc.spring.domain.Store;

public interface ReviewRepository extends JpaRepository<Review, Long>, ReviewRepositoryCustom {

Page<Review> findAllByStore(Store store, PageRequest pageRequest);
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
package umc.spring.service.StoreService;

import jakarta.persistence.criteria.CriteriaBuilder;
import org.springframework.data.domain.Page;
import umc.spring.domain.Review;
import umc.spring.domain.Store;

import java.util.List;
Expand All @@ -9,4 +12,6 @@ public interface StoreQueryService {

Optional<Store> findStore(Long id);
List<Store> findStoresByNameAndScore(String name, Float score);

Page<Review> getReviewList(Long storeId, Integer page);
}
Original file line number Diff line number Diff line change
@@ -1,9 +1,13 @@
package umc.spring.service.StoreService;

import lombok.RequiredArgsConstructor;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import umc.spring.domain.Review;
import umc.spring.domain.Store;
import umc.spring.repository.ReviewRepository.ReviewRepository;
import umc.spring.repository.StoreRepository.StoreRepository;

import java.util.List;
Expand All @@ -16,6 +20,8 @@ public class StoreQueryServiceImpl implements StoreQueryService{

private final StoreRepository storeRepository;

private final ReviewRepository reviewRepository;

@Override
public Optional<Store> findStore(Long id) {
return storeRepository.findById(id);
Expand All @@ -29,4 +35,14 @@ public List<Store> findStoresByNameAndScore(String name, Float score) {

return filteredStores;
}

@Override
public Page<Review> getReviewList(Long storeId, Integer page) {

Store store = storeRepository.findById(storeId).get();

Page<Review> storePage = reviewRepository.findAllByStore(store, PageRequest.of(page, 10));

return storePage;
}
}
17 changes: 17 additions & 0 deletions src/main/java/umc/spring/validation/annotation/ExistStore.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
package umc.spring.validation.annotation;

import jakarta.validation.Constraint;
import jakarta.validation.Payload;
import umc.spring.validation.validator.StoreExistValidator;

import java.lang.annotation.*;

@Documented
@Constraint(validatedBy = StoreExistValidator.class)
@Target( { ElementType.METHOD, ElementType.FIELD, ElementType.PARAMETER })
@Retention(RetentionPolicy.RUNTIME)
public @interface ExistStore {
String message() default "해당하는 가게가 존재하지 않습니다.";
Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default {};
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
package umc.spring.validation.validator;

import jakarta.validation.ConstraintValidator;
import jakarta.validation.ConstraintValidatorContext;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Component;
import umc.spring.apiPayload.code.status.ErrorStatus;
import umc.spring.domain.Store;
import umc.spring.service.StoreService.StoreQueryService;
import umc.spring.validation.annotation.ExistStore;

import java.util.Optional;

@Component
@RequiredArgsConstructor
public class StoreExistValidator implements ConstraintValidator<ExistStore, Long> {

private final StoreQueryService storeQueryService;

@Override
public void initialize(ExistStore constraintAnnotation) {
ConstraintValidator.super.initialize(constraintAnnotation);
}

@Override
public boolean isValid(Long value, ConstraintValidatorContext context) {
Optional<Store> target = storeQueryService.findStore(value);

if (target.isEmpty()){
context.disableDefaultConstraintViolation();
context.buildConstraintViolationWithTemplate(ErrorStatus.STORE_NOT_FOUND.toString()).addConstraintViolation();
return false;
}
return true;
}
}
43 changes: 43 additions & 0 deletions src/main/java/umc/spring/web/controller/StoreRestController.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
package umc.spring.web.controller;

import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.Parameters;
import io.swagger.v3.oas.annotations.media.Content;
import io.swagger.v3.oas.annotations.media.Schema;
import io.swagger.v3.oas.annotations.responses.ApiResponses;
import lombok.RequiredArgsConstructor;
import org.springframework.data.domain.Page;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import umc.spring.apiPayload.ApiResponse;
import umc.spring.converter.StoreConverter;
import umc.spring.domain.Review;
import umc.spring.service.StoreService.StoreQueryService;
import umc.spring.validation.annotation.ExistStore;
import umc.spring.web.dto.StoreResponseDTO;

@RestController
@RequiredArgsConstructor
@Validated
@RequestMapping("/stores")
public class StoreRestController {

private final StoreQueryService storeQueryService;

@GetMapping("/{storeId}/reviews")
@Operation(summary = "특정 가게의 리뷰 목록 조회 API",description = "특정 가게의 리뷰들의 목록을 조회하는 API이며, 페이징을 포함합니다. query String 으로 page 번호를 주세요")
@ApiResponses({
@io.swagger.v3.oas.annotations.responses.ApiResponse(responseCode = "COMMON200",description = "OK, 성공"),
@io.swagger.v3.oas.annotations.responses.ApiResponse(responseCode = "AUTH003", description = "access 토큰을 주세요!",content = @Content(schema = @Schema(implementation = ApiResponse.class))),
@io.swagger.v3.oas.annotations.responses.ApiResponse(responseCode = "AUTH004", description = "acess 토큰 만료",content = @Content(schema = @Schema(implementation = ApiResponse.class))),
@io.swagger.v3.oas.annotations.responses.ApiResponse(responseCode = "AUTH006", description = "acess 토큰 모양이 이상함",content = @Content(schema = @Schema(implementation = ApiResponse.class))),
})
@Parameters({
@Parameter(name = "storeId", description = "가게의 아이디, path variable 입니다!")
})
public ApiResponse<StoreResponseDTO.ReviewPreViewListDTO> getReviewList(@ExistStore @PathVariable(name = "storeId") Long storeId, @RequestParam(name = "page") Integer page){
Page<Review> reviewList = storeQueryService.getReviewList(storeId, page);
return ApiResponse.onSuccess(StoreConverter.reviewPreViewListDTO(reviewList));
}
}
4 changes: 4 additions & 0 deletions src/main/java/umc/spring/web/dto/StoreRequestDTO.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
package umc.spring.web.dto;

public class StoreRequestDTO {
}
36 changes: 36 additions & 0 deletions src/main/java/umc/spring/web/dto/StoreResponseDTO.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
package umc.spring.web.dto;

import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Getter;
import lombok.NoArgsConstructor;

import java.time.LocalDate;
import java.util.List;

public class StoreResponseDTO {

@Builder
@Getter
@NoArgsConstructor
@AllArgsConstructor
public static class ReviewPreViewListDTO {
List<ReviewPreViewDTO> reviewList;
Integer listSize;
Integer totalPage;
Long totalElements;
Boolean isFirst;
Boolean isLast;
}

@Builder
@Getter
@NoArgsConstructor
@AllArgsConstructor
public static class ReviewPreViewDTO {
String ownerNickname;
Float score;
String body;
LocalDate createdAt;
}
}

0 comments on commit b58d79f

Please sign in to comment.