Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -1,31 +1,38 @@
package com.example.umc9th.domain.review.controller;

import com.example.umc9th.domain.review.converter.ReviewConverter;
import com.example.umc9th.domain.review.dto.res.ReviewResDTO;
import com.example.umc9th.domain.review.entity.Review;
import com.example.umc9th.domain.review.service.ReviewQueryService;
import com.example.umc9th.domain.review.service.query.ReviewQueryServiceImpl;
import com.example.umc9th.global.apiPayload.ApiResponse;
import com.example.umc9th.global.apiPayload.code.GeneralSuccessCode;
import lombok.RequiredArgsConstructor;
import org.springframework.web.bind.annotation.GetMapping;
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.bind.annotation.*;

import java.util.List;

@RestController
@RequiredArgsConstructor
@RequestMapping("/api/reviews")
public class ReviewController {
private final ReviewQueryService reviewQueryService;
private final ReviewQueryServiceImpl reviewQueryService;


// /api/reviews/me?memberId=7
// /api/reviews/me?memberId=7&type=restaurant&query=반이학생마라탕마라반
// /api/reviews/me?memberId=7&type=rating&query=4
// /api/reviews/me?memberId=7&type=both&query=반이학생마라탕마라반&4
@GetMapping("/me")
public List<Review> myReviews(
public ApiResponse<ReviewResDTO.MyReviews> myReviews(
@RequestParam Long memberId,
@RequestParam(required = false) String type,
@RequestParam(required = false) String query
) {
return reviewQueryService.searchMyReviews(memberId, type, query);
List<Review> list = reviewQueryService.searchMyReviews(memberId, type, query);
ReviewResDTO.MyReviews dto = ReviewConverter.toMyReviews(list);
return ApiResponse.onSuccess(GeneralSuccessCode.OK, dto);
}



}
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
package com.example.umc9th.domain.review.converter;

import com.example.umc9th.domain.review.dto.res.ReviewResDTO;
import com.example.umc9th.domain.review.entity.Review;

import java.util.ArrayList;
import java.util.List;

public class ReviewConverter {
public static ReviewResDTO.MyReview toMyReview(Review review) {
return ReviewResDTO.MyReview.builder()
.reviewId(review.getId())
.restaurantName(review.getRestaurant().getRestaurantName())
.content(review.getReviewContent())
.rating(review.getRating())
.createdAt(review.getCreatedAt())
.build();
}

public static ReviewResDTO.MyReviews toMyReviews(List<Review> list) {
List<ReviewResDTO.MyReview> reviews = new ArrayList<>();
if (list != null) {
for (Review review : list) {
if (review != null) {
reviews.add(toMyReview(review));
}
}
}
return ReviewResDTO.MyReviews.builder()
.reviews(reviews)
.totalCount(reviews.size())
.build();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
package com.example.umc9th.domain.review.dto.res;

import lombok.Builder;
import lombok.Getter;
import java.time.LocalDateTime;
import java.util.List;

public class ReviewResDTO {

@Builder
@Getter
public static class MyReview {
private Long reviewId;
private String restaurantName;
private String content;
private Double rating;
private LocalDateTime createdAt;
}

@Builder
@Getter
public static class MyReviews {
private List<MyReview> reviews;
private Integer totalCount;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package com.example.umc9th.domain.review.exception;

import com.example.umc9th.global.apiPayload.code.BaseErrorCode;
import com.example.umc9th.global.apiPayload.exception.GeneralException;

public class ReviewException extends GeneralException {
public ReviewException(BaseErrorCode code) {
super(code);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
package com.example.umc9th.domain.review.exception.code;

import com.example.umc9th.global.apiPayload.code.BaseErrorCode;
import lombok.AllArgsConstructor;
import lombok.Getter;
import org.springframework.http.HttpStatus;

@Getter
@AllArgsConstructor
public enum ReviewErrorCode implements BaseErrorCode {
INVALID_TYPE(HttpStatus.BAD_REQUEST, "REVIEW400_1", "type 값이 올바르지 않습니다. [restaurant|rating|both|all]"),
MISSING_QUERY(HttpStatus.BAD_REQUEST, "REVIEW400_2", "해당 type에 필요한 query 값이 없습니다."),
INVALID_BOTH_QUERY(HttpStatus.BAD_REQUEST, "REVIEW400_3", "both 타입의 query는 '이름&숫자' 형식이어야 합니다."),
INVALID_RATING(HttpStatus.BAD_REQUEST, "REVIEW400_4", "rating은 0.0 ~ 5.0 사이의 숫자여야 합니다."),;

private final HttpStatus status;
private final String code;
private final String message;
}

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package com.example.umc9th.domain.review.service.query;

import com.example.umc9th.domain.review.entity.Review;

import java.util.List;

public interface ReviewQueryService {
List<Review> searchMyReviews(Long memberId, String type, String query);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
package com.example.umc9th.domain.review.service.query;

import com.example.umc9th.domain.review.entity.QReview;
import com.example.umc9th.domain.review.entity.Review;
import com.example.umc9th.domain.review.exception.ReviewException;
import com.example.umc9th.domain.review.exception.code.ReviewErrorCode;
import com.example.umc9th.domain.review.repository.ReviewRepository;
import com.querydsl.core.BooleanBuilder;
import jakarta.transaction.Transactional;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;

import java.util.List;

@Service
@RequiredArgsConstructor
@Transactional
public class ReviewQueryServiceImpl implements ReviewQueryService {
private final ReviewRepository reviewRepository;


public List<Review> searchMyReviews(Long memberId, String type, String query){
String safeType = (type == null) ? "all" : type.trim();
String safeQuery = (query == null) ? "" : query.trim();

QReview review = QReview.review;

// BooleanBuilder 선언
BooleanBuilder builder = new BooleanBuilder();

// BooleanBuilder 사용

// 1) 내가 작성한 리뷰
builder.and(review.member.id.eq(memberId));

// 동적 쿼리 : 조회 조건
switch (type) {
case "all" -> {
// 추가 필터 없음
}
case "restaurant" -> {
if (query.isEmpty()) throw new ReviewException(ReviewErrorCode.MISSING_QUERY);
builder.and(review.restaurant.restaurantName.contains(query));
}
case "rating" -> {
if (query.isEmpty()) throw new ReviewException(ReviewErrorCode.MISSING_QUERY);
applyRatingFilterOrThrow(builder, review, query);
}
case "both" -> {
if (query.isEmpty() || !query.contains("&")) {
throw new ReviewException(ReviewErrorCode.INVALID_BOTH_QUERY);
}
String[] parts = query.split("&", 2);
String name = parts[0].trim();
String ratingStr = parts[1].trim();
if (name.isEmpty() || ratingStr.isEmpty()) {
throw new ReviewException(ReviewErrorCode.INVALID_BOTH_QUERY);
}
builder.and(review.restaurant.restaurantName.contains(name));
applyRatingFilterOrThrow(builder, review, ratingStr);
}
default -> throw new ReviewException(ReviewErrorCode.INVALID_TYPE);
}


List<Review> reviewList = reviewRepository.searchReview(builder);

return reviewList;
}

private void applyRatingFilterOrThrow(BooleanBuilder builder, QReview review, String ratingText) {
if (ratingText == null || ratingText.isEmpty()) return;

double base;
try {
base = Double.parseDouble(ratingText);
} catch (NumberFormatException e) {
throw new ReviewException(ReviewErrorCode.INVALID_RATING);
}

if (base < 0.0 || base > 5.0) {
throw new ReviewException(ReviewErrorCode.INVALID_RATING);
}

if (base >= 5.0) {
builder.and(review.rating.eq(5.0));
} else if (base >= 0.0) {
double lower = base;
double upper = Math.min(5.0, base + 1.0);
builder.and(review.rating.goe(lower));
builder.and(review.rating.lt(upper));
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
package com.example.umc9th.domain.test.controller;

import com.example.umc9th.domain.test.converter.TestConverter;
import com.example.umc9th.domain.test.dto.res.TestResDTO;
import com.example.umc9th.domain.test.service.query.TestQueryService;
import com.example.umc9th.global.apiPayload.ApiResponse;
import com.example.umc9th.global.apiPayload.code.GeneralSuccessCode;
import lombok.RequiredArgsConstructor;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

@RestController
@RequiredArgsConstructor
@RequestMapping("/temp")
public class TestController {
private final TestQueryService testQueryService;

@GetMapping("/test")
public ApiResponse<TestResDTO.Testing> test() throws Exception {
// 응답 코드 정의
GeneralSuccessCode code = GeneralSuccessCode.OK;
return ApiResponse.onSuccess(
code,
TestConverter.toTestingDTO("This is Test!")
);
}

// 예외 상황
@GetMapping("/exception")
public ApiResponse<TestResDTO.Exception> exception(
@RequestParam Long flag
) {
testQueryService.checkFlag(flag);

// 응답 코드 정의
GeneralSuccessCode code = GeneralSuccessCode.OK;
return ApiResponse.onSuccess(code, TestConverter.toExceptionDTO("This is Test!"));
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
package com.example.umc9th.domain.test.converter;

import com.example.umc9th.domain.test.dto.res.TestResDTO;

public class TestConverter {
// 객체 -> DTO
public static TestResDTO.Testing toTestingDTO(
String testing
) {
return TestResDTO.Testing.builder()
.testString(testing)
.build();
}


// 객체 -> DTO
public static TestResDTO.Exception toExceptionDTO(
String testing
){
return TestResDTO.Exception.builder()
.testString(testing)
.build();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
package com.example.umc9th.domain.test.dto.req;

public class TestReqDTO {
}
Loading