diff --git a/src/main/java/ssu/eatssu/domain/auth/dto/AppleLoginRequestV2.java b/src/main/java/ssu/eatssu/domain/auth/dto/AppleLoginRequestV2.java
new file mode 100644
index 00000000..33042483
--- /dev/null
+++ b/src/main/java/ssu/eatssu/domain/auth/dto/AppleLoginRequestV2.java
@@ -0,0 +1,15 @@
+package ssu.eatssu.domain.auth.dto;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import ssu.eatssu.domain.user.entity.DeviceType;
+import ssu.eatssu.global.log.annotation.LogMask;
+
+// 애플 계정을 통해서 갤럭시 기기에도 접속을 할 수도 있다고 생각해서, DeviceType을 받도록 설계
+@Schema(title = "애플 로그인 및 회원가입 V2")
+public record AppleLoginRequestV2(
+ @LogMask
+ @Schema(description = "identityToken", example = "eyJraWQiOiJXNldjT0tCIiwiYWxnIjoi...")
+ String identityToken,
+ @Schema(description = "deviceType", example = "IOS")
+ DeviceType deviceType
+) {}
diff --git a/src/main/java/ssu/eatssu/domain/auth/dto/KakaoLoginRequestV2.java b/src/main/java/ssu/eatssu/domain/auth/dto/KakaoLoginRequestV2.java
new file mode 100644
index 00000000..20baf406
--- /dev/null
+++ b/src/main/java/ssu/eatssu/domain/auth/dto/KakaoLoginRequestV2.java
@@ -0,0 +1,21 @@
+package ssu.eatssu.domain.auth.dto;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import jakarta.validation.constraints.Email;
+import jakarta.validation.constraints.NotBlank;
+import ssu.eatssu.domain.user.entity.DeviceType;
+import ssu.eatssu.global.log.annotation.LogMask;
+
+@Schema(title = "카카오 로그인 및 회원가입 V2")
+public record KakaoLoginRequestV2(
+ @LogMask
+ @NotBlank(message = "이메일을 입력해주세요.")
+ @Email(message = "올바른 이메일 주소를 입력해주세요.")
+ @Schema(description = "이메일", example = "test@email.com")
+ String email,
+ @LogMask
+ @Schema(description = "providerId", example = "10378247832195")
+ String providerId,
+ @Schema(description = "deviceType", example = "IOS")
+ DeviceType deviceType
+) {}
diff --git a/src/main/java/ssu/eatssu/domain/auth/infrastructure/SecurityConfig.java b/src/main/java/ssu/eatssu/domain/auth/infrastructure/SecurityConfig.java
index 959cfc3a..3072cee1 100644
--- a/src/main/java/ssu/eatssu/domain/auth/infrastructure/SecurityConfig.java
+++ b/src/main/java/ssu/eatssu/domain/auth/infrastructure/SecurityConfig.java
@@ -27,7 +27,7 @@ public class SecurityConfig {
};
private static final String[] AUTH_WHITELIST = {
- "/", "/oauths/kakao", "/oauths/apple", "/menus/**", "/meals/**", "/admin/login",
+ "/", "/oauths/kakao", "/oauths/apple", "/menus/**", "/meals/**", "/admin/login", "/oauths/v2/kakao","/oauths/v2/apple",
"/reviews", "/reviews/menus/**", "/reviews/meals/**", "/v2/reviews/statistics/**",
"/v2/reviews/menus/**", "/v2/reviews/meals/**", "/actuator/**", "/error-test/**"
};
diff --git a/src/main/java/ssu/eatssu/domain/auth/presentation/OAuthController.java b/src/main/java/ssu/eatssu/domain/auth/presentation/OAuthController.java
index 1ba3868f..d425ae70 100644
--- a/src/main/java/ssu/eatssu/domain/auth/presentation/OAuthController.java
+++ b/src/main/java/ssu/eatssu/domain/auth/presentation/OAuthController.java
@@ -13,9 +13,7 @@
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
-import ssu.eatssu.domain.auth.dto.AppleLoginRequest;
-import ssu.eatssu.domain.auth.dto.KakaoLoginRequest;
-import ssu.eatssu.domain.auth.dto.ValidRequest;
+import ssu.eatssu.domain.auth.dto.*;
import ssu.eatssu.domain.auth.service.OAuthService;
import ssu.eatssu.domain.user.dto.Tokens;
import ssu.eatssu.global.handler.response.BaseResponse;
@@ -31,6 +29,7 @@ public class OAuthController {
private final OAuthService oauthService;
+ // TODO : 로그인 & 회원 가입 마이그레이션 이후에 지울 것.
@Operation(summary = "카카오 회원가입, 로그인 [인증 토큰 필요 X]", description = """
카카오 회원가입, 로그인 API 입니다.
가입된 회원일 경우 카카오 로그인, 미가입 회원일 경우 회원가입 후 자동 로그인됩니다.
@@ -49,6 +48,25 @@ public BaseResponse kakaoLogin(@Valid @RequestBody KakaoLoginRequest req
return BaseResponse.success(tokens);
}
+ @Operation(summary = "카카오 회원가입, 로그인 V2 [인증 토큰 필요 X]", description = """
+ 카카오 회원가입, 로그인 V2 API 입니다.
+ 가입된 회원일 경우 카카오 로그인, 미가입 회원일 경우 회원가입 후 자동 로그인됩니다.
+ """)
+ @ApiResponses(value = {
+ @ApiResponse(responseCode = "200", description = "카카오 회원가입/로그인 성공")
+ })
+ @PostMapping("/v2/kakao")
+ public BaseResponse kakaoLoginV2(@Valid @RequestBody KakaoLoginRequestV2 request) {
+ long startTime = System.currentTimeMillis();
+ Tokens tokens = oauthService.kakaoLoginV2(request);
+ long endTime = System.currentTimeMillis();
+ long duration = endTime - startTime;
+ log.info("OAuthWarmupRunner 완료 - 소요 시간: {} ms", duration);
+
+ return BaseResponse.success(tokens);
+ }
+
+ // TODO : 로그인 & 회원 가입 마이그레이션 이후에 지울 것.
@Operation(summary = "애플 회원가입, 로그인 [인증 토큰 필요 X]", description = """
애플 로그인, 회원가입 API 입니다.
가입된 회원일 경우 카카오 로그인, 미가입 회원일 경우 회원가입 후 자동 로그인됩니다.
@@ -62,6 +80,19 @@ public BaseResponse appleLogin(@Valid @RequestBody AppleLoginRequest req
return BaseResponse.success(tokens);
}
+ @Operation(summary = "애플 회원가입, 로그인 V2 [인증 토큰 필요 X]", description = """
+ 애플 로그인, 회원가입 API V2 입니다.
+ 가입된 회원일 경우 카카오 로그인, 미가입 회원일 경우 회원가입 후 자동 로그인됩니다.
+ """)
+ @ApiResponses(value = {
+ @ApiResponse(responseCode = "200", description = "애플 회원가입/로그인 성공")
+ })
+ @PostMapping("/v2/apple")
+ public BaseResponse appleLoginV2(@Valid @RequestBody AppleLoginRequestV2 request) {
+ Tokens tokens = oauthService.appleLoginV2(request);
+ return BaseResponse.success(tokens);
+ }
+
@Operation(summary = "토큰 재발급", description = "accessToken, refreshToken 재발급 API 입니다.")
@ApiResponses(value = {
@ApiResponse(responseCode = "200", description = "토큰 재발급 성공")
diff --git a/src/main/java/ssu/eatssu/domain/auth/security/CustomUserDetails.java b/src/main/java/ssu/eatssu/domain/auth/security/CustomUserDetails.java
index eb05c2a5..f24179e3 100644
--- a/src/main/java/ssu/eatssu/domain/auth/security/CustomUserDetails.java
+++ b/src/main/java/ssu/eatssu/domain/auth/security/CustomUserDetails.java
@@ -5,6 +5,7 @@
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;
+import ssu.eatssu.domain.user.entity.DeviceType;
import ssu.eatssu.domain.user.entity.User;
import java.util.ArrayList;
@@ -17,12 +18,14 @@ public class CustomUserDetails implements UserDetails {
private final String email;
private final String credentials;
private final GrantedAuthority role;
+ private final DeviceType deviceType;
public CustomUserDetails(User user) {
this.id = user.getId();
this.email = user.getEmail();
this.credentials = user.getCredentials();
this.role = user.getRole();
+ this.deviceType = user.getDeviceType();
}
@Override
diff --git a/src/main/java/ssu/eatssu/domain/auth/security/JwtAuthenticationFilter.java b/src/main/java/ssu/eatssu/domain/auth/security/JwtAuthenticationFilter.java
index 327108b4..46dab1fb 100644
--- a/src/main/java/ssu/eatssu/domain/auth/security/JwtAuthenticationFilter.java
+++ b/src/main/java/ssu/eatssu/domain/auth/security/JwtAuthenticationFilter.java
@@ -31,8 +31,8 @@ public class JwtAuthenticationFilter extends GenericFilterBean {
"/v2/reviews/menus/**", "/v2/reviews/meals/**", "/actuator/**", "/error-test/**",
"/swagger-ui.html", "/swagger-ui/**", "/v3/api-docs/**", "/swagger-resources/**",
"/oauths/valid/token", "/admin/img/**", "/css/**", "/js/**",
- "/favicon.ico", "/error/**", "/webjars/**", "/h2-console/**"
- );
+ "/favicon.ico", "/error/**", "/webjars/**", "/h2-console/**",
+ "/oauths/v2/kakao", "/oauths/v2/apple");
private final JwtTokenProvider jwtTokenProvider;
@Override
diff --git a/src/main/java/ssu/eatssu/domain/auth/security/JwtTokenProvider.java b/src/main/java/ssu/eatssu/domain/auth/security/JwtTokenProvider.java
index e13ffaa1..2efd9a99 100644
--- a/src/main/java/ssu/eatssu/domain/auth/security/JwtTokenProvider.java
+++ b/src/main/java/ssu/eatssu/domain/auth/security/JwtTokenProvider.java
@@ -146,7 +146,7 @@ public Authentication getAuthentication(String token) throws JsonProcessingExcep
UserPrincipalDto userPrincipalDto = objectMapper.readValue(claims.getSubject(), UserPrincipalDto.class);
CustomUserDetails principal = new CustomUserDetails(userPrincipalDto.getId(), userPrincipalDto.getEmail(), "",
- authorities.get(0));
+ authorities.get(0),userPrincipalDto.getDeviceType());
return new UsernamePasswordAuthenticationToken(principal, token, authorities);
}
diff --git a/src/main/java/ssu/eatssu/domain/auth/security/UserPrincipalDto.java b/src/main/java/ssu/eatssu/domain/auth/security/UserPrincipalDto.java
index d2d189e6..163756f7 100644
--- a/src/main/java/ssu/eatssu/domain/auth/security/UserPrincipalDto.java
+++ b/src/main/java/ssu/eatssu/domain/auth/security/UserPrincipalDto.java
@@ -4,6 +4,7 @@
import lombok.Builder;
import lombok.Getter;
import lombok.NoArgsConstructor;
+import ssu.eatssu.domain.user.entity.DeviceType;
@Getter
@Builder
@@ -12,13 +13,15 @@
public class UserPrincipalDto {
private Long id;
private String email;
+ private DeviceType deviceType;
private String role;
public static UserPrincipalDto from(CustomUserDetails userDetails) {
return UserPrincipalDto.builder()
.id(userDetails.getId())
.email(userDetails.getEmail())
+ .deviceType(userDetails.getDeviceType())
.role(userDetails.getRole().getAuthority())
.build();
}
-}
\ No newline at end of file
+}
diff --git a/src/main/java/ssu/eatssu/domain/auth/service/OAuthService.java b/src/main/java/ssu/eatssu/domain/auth/service/OAuthService.java
index 7c263e70..2d6e7ad3 100644
--- a/src/main/java/ssu/eatssu/domain/auth/service/OAuthService.java
+++ b/src/main/java/ssu/eatssu/domain/auth/service/OAuthService.java
@@ -6,15 +6,13 @@
import org.springframework.security.core.Authentication;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
-import ssu.eatssu.domain.auth.dto.AppleLoginRequest;
-import ssu.eatssu.domain.auth.dto.KakaoLoginRequest;
-import ssu.eatssu.domain.auth.dto.OAuthInfo;
-import ssu.eatssu.domain.auth.dto.ValidRequest;
+import ssu.eatssu.domain.auth.dto.*;
import ssu.eatssu.domain.auth.entity.AppleAuthenticator;
import ssu.eatssu.domain.auth.entity.OAuthProvider;
import ssu.eatssu.domain.auth.security.JwtTokenProvider;
import ssu.eatssu.domain.auth.util.RandomNicknameUtil;
import ssu.eatssu.domain.user.dto.Tokens;
+import ssu.eatssu.domain.user.entity.DeviceType;
import ssu.eatssu.domain.user.entity.User;
import ssu.eatssu.domain.user.repository.UserRepository;
import ssu.eatssu.domain.user.service.UserService;
@@ -41,6 +39,21 @@ public Tokens kakaoLogin(KakaoLoginRequest request) {
return generateOauthJwtTokens(user.getEmail(), KAKAO, request.providerId());
}
+ /**
+ * V1 -> V2로 넘어가면서 DeviceType(IOS,ANDROID) 정보를 추가로 받게 되었고, 기존에 가입한 유저들은 추가로 기입해 주게 됩니다.
+ */
+ public Tokens kakaoLoginV2(KakaoLoginRequestV2 request) {
+ User user = userRepository.findByProviderId(request.providerId())
+ .orElseGet(() -> userService.joinV2(request.email(), KAKAO, request.providerId(),request.deviceType()));
+
+ if (user.getDeviceType() == null) {
+ user.updateDeviceType(request.deviceType());
+ }
+
+ return generateOauthJwtTokens(user.getEmail(), KAKAO, request.providerId());
+ }
+
+
public Tokens appleLogin(AppleLoginRequest request) {
OAuthInfo oAuthInfo = appleAuthenticator.getOAuthInfoByIdentityToken(request.identityToken());
@@ -52,6 +65,24 @@ public Tokens appleLogin(AppleLoginRequest request) {
return generateOauthJwtTokens(user.getEmail(), APPLE, oAuthInfo.providerId());
}
+ /**
+ * V1 -> V2로 넘어가면서 DeviceType(IOS,ANDROID) 정보를 추가로 받게 되었고, 기존에 가입한 유저들은 추가로 기입해 주게 됩니다.
+ */
+ public Tokens appleLoginV2(AppleLoginRequestV2 request) {
+ OAuthInfo oAuthInfo = appleAuthenticator.getOAuthInfoByIdentityToken(request.identityToken());
+
+ User user = userRepository.findByProviderId(oAuthInfo.providerId())
+ .orElseGet(() -> userService.joinV2(oAuthInfo.email(), APPLE, oAuthInfo.providerId(),request.deviceType()));
+
+ updateAppleUserEmail(user, oAuthInfo.email());
+
+ if (user.getDeviceType() == null) {
+ user.updateDeviceType(request.deviceType());
+ }
+
+ return generateOauthJwtTokens(user.getEmail(), APPLE, oAuthInfo.providerId());
+ }
+
public Tokens refreshTokens(Authentication authentication) {
return jwtTokenProvider.generateTokens(authentication);
}
@@ -97,5 +128,4 @@ private Tokens generateOauthJwtTokens(String email, OAuthProvider provider, Stri
private String makeOauthCredentials(OAuthProvider provider, String providerId) {
return provider + providerId;
}
-
}
diff --git a/src/main/java/ssu/eatssu/domain/inquiry/presentation/InquiryController.java b/src/main/java/ssu/eatssu/domain/inquiry/presentation/InquiryController.java
index b5e346b9..081a050b 100644
--- a/src/main/java/ssu/eatssu/domain/inquiry/presentation/InquiryController.java
+++ b/src/main/java/ssu/eatssu/domain/inquiry/presentation/InquiryController.java
@@ -21,6 +21,10 @@
import ssu.eatssu.domain.slack.service.SlackService;
import ssu.eatssu.global.handler.response.BaseResponse;
+/**
+ * 문의하기는 카카오톡으로 이동되어 사용되지 않고 있습니다.
+ */
+@Deprecated
@RestController
@RequiredArgsConstructor
@RequestMapping("/inquiries")
diff --git a/src/main/java/ssu/eatssu/domain/review/dto/MealReviewResponse.java b/src/main/java/ssu/eatssu/domain/review/dto/MealReviewResponse.java
index b6eeca59..7412264d 100644
--- a/src/main/java/ssu/eatssu/domain/review/dto/MealReviewResponse.java
+++ b/src/main/java/ssu/eatssu/domain/review/dto/MealReviewResponse.java
@@ -1,17 +1,17 @@
package ssu.eatssu.domain.review.dto;
import io.swagger.v3.oas.annotations.media.Schema;
-import lombok.AllArgsConstructor;
-import lombok.Builder;
-import lombok.Getter;
-import ssu.eatssu.domain.review.entity.Review;
-import ssu.eatssu.domain.review.entity.ReviewMenuLike;
-
import java.time.LocalDate;
import java.util.ArrayList;
+import java.util.Collections;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;
+import lombok.AllArgsConstructor;
+import lombok.Builder;
+import lombok.Getter;
+import ssu.eatssu.domain.review.entity.Review;
+import ssu.eatssu.domain.review.entity.ReviewMenuLike;
@AllArgsConstructor
@Builder
@@ -31,7 +31,7 @@ public class MealReviewResponse {
private String writerNickname;
@Schema(description = "평점", example = "4")
- private Double rating;
+ private Integer rating;
@Schema(description = "리뷰 작성 날짜(format = yyyy-MM-dd)", example = "2023-04-07")
private LocalDate writtenAt;
@@ -60,26 +60,40 @@ public class MealReviewResponse {
public static MealReviewResponse from(Review review,
Long userId,
- List validMenus,Double rating) {
+ List validMenus, Integer rating) {
List imageUrls = new ArrayList<>();
review.getReviewImages().forEach(i -> imageUrls.add(i.getImageUrl()));
- // 좋아요한 메뉴 ID 모음
- Set likedMenuIds = review.getMenuLikes().stream()
- .filter(ReviewMenuLike::getIsLike)
- .map(like -> like.getMenu().getId())
- .collect(Collectors.toSet());
-
- List menuNames = validMenus.stream()
- .map(valid -> new MenuIdNameLikeDto(
- valid.getMenuId(),
- valid.getName(),
- likedMenuIds.contains(valid.getMenuId())
- ))
- .toList();
+ List menuNames;
+ if (review.getMeal() != null) {
+ Set likedMenuIds = review.getMenuLikes().stream()
+ .filter(ReviewMenuLike::getIsLike)
+ .map(like -> like.getMenu().getId())
+ .collect(Collectors.toSet());
+
+ menuNames = validMenus.stream()
+ .map(valid -> new MenuIdNameLikeDto(
+ valid.getMenuId(),
+ valid.getName(),
+ likedMenuIds.contains(valid.getMenuId())
+ ))
+ .toList();
+ } else if (review.getMenu() != null) {
+ menuNames = Collections.singletonList(
+ new MenuIdNameLikeDto(review.getMenu().getId(),
+ review.getMenu().getName(),
+ false)
+ );
+ } else {
+ menuNames = Collections.emptyList();
+ }
+ Integer resolvedRating = (review.getRating() != null)
+ ? review.getRating()
+ : (review.getRatings() != null ? review.getRatings().getMainRating() : null);
+
MealReviewResponseBuilder builder = MealReviewResponse.builder()
.reviewId(review.getId())
- .rating(rating)
+ .rating(resolvedRating)
.writtenAt(review.getCreatedDate().toLocalDate())
.content(review.getContent())
.imageUrls(imageUrls)
diff --git a/src/main/java/ssu/eatssu/domain/review/dto/ReviewDetail.java b/src/main/java/ssu/eatssu/domain/review/dto/ReviewDetail.java
index ca5979cd..32db01aa 100644
--- a/src/main/java/ssu/eatssu/domain/review/dto/ReviewDetail.java
+++ b/src/main/java/ssu/eatssu/domain/review/dto/ReviewDetail.java
@@ -1,6 +1,11 @@
package ssu.eatssu.domain.review.dto;
import io.swagger.v3.oas.annotations.media.Schema;
+import java.time.LocalDate;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Set;
+import java.util.stream.Collectors;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Getter;
@@ -8,12 +13,6 @@
import ssu.eatssu.domain.review.entity.Review;
import ssu.eatssu.domain.review.entity.ReviewMenuLike;
-import java.time.LocalDate;
-import java.util.ArrayList;
-import java.util.List;
-import java.util.Set;
-import java.util.stream.Collectors;
-
@AllArgsConstructor
@Builder
@Schema(title = "리뷰 상세")
@@ -58,9 +57,13 @@ public static ReviewDetail from(Review review, Long userId) {
.map(like -> like.getMenu().getId())
.collect(Collectors.toSet());
+ Integer rating = (review.getRating() != null)
+ ? review.getRating()
+ : review.getRatings().getMainRating();
+
ReviewDetailBuilder builder = ReviewDetail.builder()
.reviewId(review.getId())
- .rating(review.getRatings().getMainRating())
+ .rating(rating)
.writtenAt(review.getCreatedDate().toLocalDate())
.content(review.getContent())
.imageUrls(imageUrls)
diff --git a/src/main/java/ssu/eatssu/domain/review/dto/UploadReviewRequest.java b/src/main/java/ssu/eatssu/domain/review/dto/UploadReviewRequest.java
index 9963d290..8d0e2d6e 100644
--- a/src/main/java/ssu/eatssu/domain/review/dto/UploadReviewRequest.java
+++ b/src/main/java/ssu/eatssu/domain/review/dto/UploadReviewRequest.java
@@ -1,7 +1,6 @@
package ssu.eatssu.domain.review.dto;
import io.swagger.v3.oas.annotations.media.Schema;
-import jakarta.validation.constraints.Max;
import jakarta.validation.constraints.Size;
import lombok.AccessLevel;
import lombok.Getter;
@@ -41,7 +40,7 @@ public UploadReviewRequest(int mainRating, String content) {
}
public Review toReviewEntity(User user, Menu menu) {
- Ratings ratings = Ratings.of(this.mainRating,this.amountRating,this.tasteRating);
+ Ratings ratings = Ratings.of(this.mainRating, this.amountRating, this.tasteRating);
return Review.builder()
.user(user)
.content(this.content)
diff --git a/src/main/java/ssu/eatssu/domain/review/repository/ReviewRepository.java b/src/main/java/ssu/eatssu/domain/review/repository/ReviewRepository.java
index d817cfcb..e4c87a4c 100644
--- a/src/main/java/ssu/eatssu/domain/review/repository/ReviewRepository.java
+++ b/src/main/java/ssu/eatssu/domain/review/repository/ReviewRepository.java
@@ -1,5 +1,7 @@
package ssu.eatssu.domain.review.repository;
+import java.util.Collection;
+import java.util.List;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.data.jpa.repository.JpaRepository;
@@ -9,9 +11,6 @@
import ssu.eatssu.domain.menu.entity.Menu;
import ssu.eatssu.domain.review.entity.Review;
-import java.util.Collection;
-import java.util.List;
-
public interface ReviewRepository extends JpaRepository, ReviewRepositoryCustom {
List findAllByMenu(Menu menu);
@@ -31,4 +30,22 @@ public interface ReviewRepository extends JpaRepository, ReviewRep
Page findReviewsByMealIds(@Param("mealIds") List mealIds,
@Param("lastReviewId") Long lastReviewId,
Pageable pageable);
+
+ @Query("""
+ SELECT r FROM Review r
+ WHERE (r.meal.id = :mealId
+ OR r.menu.id IN (SELECT mm.menu.id FROM MealMenu mm WHERE mm.meal.id = :mealId))
+ """)
+ List findAllMealAndMenuReviews(@Param("mealId") Long mealId);
+
+ @Query("""
+ SELECT r FROM Review r
+ WHERE (r.meal.id = :mealId
+ OR r.menu.id IN (SELECT mm.menu.id FROM MealMenu mm WHERE mm.meal.id = :mealId))
+ AND (:lastReviewId IS NULL OR r.id < :lastReviewId)
+ ORDER BY r.id DESC
+ """)
+ Page findMealAndMenuReviews(@Param("mealId") Long mealId,
+ @Param("lastReviewId") Long lastReviewId,
+ Pageable pageable);
}
diff --git a/src/main/java/ssu/eatssu/domain/review/service/ReviewServiceV2.java b/src/main/java/ssu/eatssu/domain/review/service/ReviewServiceV2.java
index 190ff9f5..9de77832 100644
--- a/src/main/java/ssu/eatssu/domain/review/service/ReviewServiceV2.java
+++ b/src/main/java/ssu/eatssu/domain/review/service/ReviewServiceV2.java
@@ -1,5 +1,17 @@
package ssu.eatssu.domain.review.service;
+import static ssu.eatssu.global.handler.response.BaseResponseStatus.NOT_FOUND_MEAL;
+import static ssu.eatssu.global.handler.response.BaseResponseStatus.NOT_FOUND_MENU;
+import static ssu.eatssu.global.handler.response.BaseResponseStatus.NOT_FOUND_REVIEW;
+import static ssu.eatssu.global.handler.response.BaseResponseStatus.NOT_FOUND_USER;
+import static ssu.eatssu.global.handler.response.BaseResponseStatus.REVIEW_PERMISSION_DENIED;
+
+import java.util.Collections;
+import java.util.List;
+import java.util.Map;
+import java.util.Objects;
+import java.util.Optional;
+import java.util.stream.Collectors;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.ApplicationEventPublisher;
@@ -23,13 +35,13 @@
import ssu.eatssu.domain.review.dto.MenuIdNameDto;
import ssu.eatssu.domain.review.dto.MenuLikeRequest;
import ssu.eatssu.domain.review.dto.MenuReviewsV2Response;
+import ssu.eatssu.domain.review.dto.RatingAverages;
import ssu.eatssu.domain.review.dto.RestaurantReviewResponse;
import ssu.eatssu.domain.review.dto.ReviewDetail;
import ssu.eatssu.domain.review.dto.ReviewRatingCount;
import ssu.eatssu.domain.review.dto.UpdateMealReviewRequest;
import ssu.eatssu.domain.review.dto.ValidMenuForViewResponse;
import ssu.eatssu.domain.review.entity.Review;
-import ssu.eatssu.domain.review.repository.ReviewImageRepository;
import ssu.eatssu.domain.review.repository.ReviewRepository;
import ssu.eatssu.domain.review.utils.MenuFilterUtil;
import ssu.eatssu.domain.slice.dto.SliceResponse;
@@ -39,19 +51,6 @@
import ssu.eatssu.global.handler.response.BaseException;
import ssu.eatssu.global.log.event.LogEvent;
-import java.util.Collections;
-import java.util.List;
-import java.util.Map;
-import java.util.Objects;
-import java.util.Optional;
-import java.util.stream.Collectors;
-
-import static ssu.eatssu.global.handler.response.BaseResponseStatus.NOT_FOUND_MEAL;
-import static ssu.eatssu.global.handler.response.BaseResponseStatus.NOT_FOUND_MENU;
-import static ssu.eatssu.global.handler.response.BaseResponseStatus.NOT_FOUND_REVIEW;
-import static ssu.eatssu.global.handler.response.BaseResponseStatus.NOT_FOUND_USER;
-import static ssu.eatssu.global.handler.response.BaseResponseStatus.REVIEW_PERMISSION_DENIED;
-
@Slf4j
@Service
@RequiredArgsConstructor
@@ -61,9 +60,7 @@ public class ReviewServiceV2 {
private final MenuRepository menuRepository;
private final MealRepository mealRepository;
private final MealMenuRepository mealMenuRepository;
- private final ReviewImageRepository reviewImageRepository;
private final ApplicationEventPublisher eventPublisher;
-
private final MealRatingService mealRatingService;
/**
@@ -207,14 +204,8 @@ public SliceResponse findMealReviewList(Long mealId, Long la
return SliceResponse.empty();
}
- List validMenuIds = validMenus.stream().map(ValidMenuForViewResponse.MenuDto::getMenuId).toList();
- List mealIds = mealMenuRepository.findMealIdsByMenuIds(validMenuIds);
- if (mealIds.isEmpty()) {
- log.warn("No related mealIds found for validMenuIds={} in mealId={}", validMenuIds, mealId);
- return SliceResponse.empty();
- }
-
- Page pageReviews = reviewRepository.findReviewsByMealIds(mealIds, lastReviewId, pageable);
+ Page pageReviews = reviewRepository.findMealAndMenuReviews(mealId, lastReviewId,
+ pageable);
Long userId = (userDetails != null) ? userDetails.getId() : null;
@@ -222,7 +213,7 @@ public SliceResponse findMealReviewList(Long mealId, Long la
pageReviews.getContent()
.stream()
.map(review -> MealReviewResponse.from(review,
- userId, validMenus,mealRatingService.getMainRatingAverage(review.getMeal().getId())))
+ userId, validMenus, review.getRating()))
.collect(Collectors.toList());
return SliceResponse.builder()
@@ -308,15 +299,15 @@ public MenuReviewsV2Response findMenuReviews(Long menuId) {
*/
public MealReviewsV2Response findMealReviews(Long mealId) {
Meal meal = mealRepository.findById(mealId).orElseThrow(() -> new BaseException(NOT_FOUND_MEAL));
- List reviews = reviewRepository.findAllByMeal(meal);
+ List combinedReviews = reviewRepository.findAllMealAndMenuReviews(mealId);
List