Skip to content
Merged
Show file tree
Hide file tree
Changes from 19 commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
9d471b9
🔨chore : gitignore sql 추가
doup2001 Aug 4, 2025
0d245a5
📂 file : 해시태그 도메인 폴더 분리
doup2001 Aug 4, 2025
8490cdb
♻️ refactor : projectionDTO 인터페이스화
doup2001 Aug 4, 2025
74dd3f7
♻️ refactor : projectionDTO 인터페이스화에 따른 유저서비스 수정
doup2001 Aug 4, 2025
bbc2c06
📂 file : 컨버터 폴더 분리
doup2001 Aug 4, 2025
38eda17
💡 style : DTO 파일명 수정
doup2001 Aug 4, 2025
bb00c6f
♻️ refactor : 피드백 DTO 스웨거 관련 내용 추가
doup2001 Aug 4, 2025
2724853
📂 file : 검색 관련 레포지토리 폴더 이동
doup2001 Aug 4, 2025
b20c018
✨ feat : 좌석과 리뷰의 다대다 연결을 위한 중간 테이블 구현
doup2001 Aug 4, 2025
64f98dc
♻️ refactor : 기존 리뷰 테이블 수정
doup2001 Aug 4, 2025
7758a7d
♻️ refactor : 다대다에 따른 리뷰 서비스 수정
doup2001 Aug 4, 2025
60743e8
♻️ refactor : Query에서 발생하는 Collect 문제 해결
doup2001 Aug 4, 2025
302a496
♻️ refactor : 삭제할때 ID 바탕으로 삭제하도록 수정
doup2001 Aug 4, 2025
8bc2684
✅ test : 다대다에 맞춰 테스트 수정
doup2001 Aug 4, 2025
3e56be9
📂 file : 필요없는 파일 삭제
doup2001 Aug 4, 2025
ecad4b2
♻️ refactor : 리뷰 해시태그 2번 조회되는 문제 해결
doup2001 Aug 4, 2025
847550f
♻️ refactor : 검색 관련 기능 수정 및 테스트 구현
doup2001 Aug 4, 2025
83ca622
♻️ refactor : 마이페이지 레벨 관련 기능 수정 및 테스트 구현
doup2001 Aug 4, 2025
282fb6c
✨ feat : 리뷰 생성시, 프런트에 ID 전달
doup2001 Aug 4, 2025
8e0671b
♻️ refactor : DTO 서비스 내에서 변환하도록 수정
doup2001 Aug 4, 2025
b3a33e3
♻️ refactor : 테스트 통과를 위해 임시적으로 수정, 추후 서비스 레이어에서 처리하도록 변경 필요
doup2001 Aug 4, 2025
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
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ build/
!**/src/test/**/build/
*.yml
.env
data.sql

### STS ###
.apt_generated
Expand Down
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

인기 리뷰 리스트를 제공하기 위한 DTO 구성과 BestReviewSnapshot 또는 Review+해시태그+좋아요 수 기반 생성 로직을 모두 분리해서 제공한 점이 좋은 것 같습니다.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

감사합니다!!

Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
package com.seeat.server.domain.best.application.dto.response;

import com.seeat.server.domain.best.domain.entity.BestReviewSnapshot;
import com.seeat.server.domain.review.domain.entity.HashTag;
import com.seeat.server.domain.hashtag.domain.entity.HashTag;
import com.seeat.server.domain.review.domain.entity.Review;
import com.seeat.server.domain.review.domain.entity.ReviewHashTag;
import com.seeat.server.domain.hashtag.domain.entity.ReviewHashTag;
import com.seeat.server.domain.theater.domain.entity.Auditorium;
import com.seeat.server.domain.theater.domain.entity.Seat;
import com.seeat.server.domain.user.domain.entity.User;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Builder;
Expand Down Expand Up @@ -98,7 +100,10 @@ public static List<BestReviewListResponse> from(List<BestReviewSnapshot> snapsho
}

/// DB에서 직접 조회 정적 팩토리 메서드
public static BestReviewListResponse from(Review review, List<ReviewHashTag> hashTags, Long heartCount) {
public static BestReviewListResponse from(Review review, List<ReviewHashTag> hashTags, Long heartCount, List<Seat> seats) {

/// 좌석에 따른 상영관
Auditorium auditorium = seats.get(0).getAuditorium();

/// 해시태그들 정리
List<String> hashtags = hashTags.stream()
Expand All @@ -114,7 +119,7 @@ public static BestReviewListResponse from(Review review, List<ReviewHashTag> has
.thumbnailUrl(review.getThumbnailUrl())
.hashtags(hashtags)
.movieTitle(review.getMovieTitle())
.theaterName(review.getSeat().getAuditorium().getTheater().getName())
.theaterName(auditorium.getTheater().getName() + auditorium.getName())
.title(review.getTitle())
.content(review.getContent())
.userId(user.getId())
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
package com.seeat.server.domain.review.application.dto.response;
package com.seeat.server.domain.hashtag.application.dto.response;

import com.seeat.server.domain.review.domain.entity.ReviewHashTag;
import com.seeat.server.domain.review.domain.repository.dto.ReviewHashTagWithCount;
import com.seeat.server.domain.hashtag.domain.repository.ReviewHashTagWithCount;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Builder;
import java.util.List;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
package com.seeat.server.domain.review.application.dto.response;
package com.seeat.server.domain.hashtag.application.dto.response;

import com.seeat.server.domain.review.domain.entity.HashTag;
import com.seeat.server.domain.hashtag.domain.entity.HashTag;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Builder;
import java.util.List;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
package com.seeat.server.domain.review.application.dto.response;
package com.seeat.server.domain.hashtag.application.dto.response;


import com.seeat.server.domain.review.domain.entity.ReviewHashTag;
import com.seeat.server.domain.hashtag.domain.entity.ReviewHashTag;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Builder;

Expand Down
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

HashTagService는 해시태그를 정렬해서 응답하는 단순하고 명확한 책임을 잘 수행하고 있어요.
특히 Comparator.comparing(HashTag::getType)으로 타입 기준 정렬하고, DTO 변환을 분리한 점은 확장성 측면에서도 좋은 것 같습니다!

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

감사합니다!! 필수 해시태그가 있기에 이렇게 수정해두었습니다!!

Original file line number Diff line number Diff line change
@@ -1,21 +1,15 @@
package com.seeat.server.domain.review.application.service;

import com.seeat.server.domain.review.application.dto.response.HashTagResponse;
import com.seeat.server.domain.review.application.usecase.HashTagUseCase;
import com.seeat.server.domain.review.domain.entity.HashTag;
import com.seeat.server.domain.review.domain.entity.Review;
import com.seeat.server.domain.review.domain.entity.ReviewHashTag;
import com.seeat.server.domain.review.domain.repository.HashTagRepository;
import com.seeat.server.domain.review.domain.repository.ReviewHashTagRepository;
package com.seeat.server.domain.hashtag.application.service;

import com.seeat.server.domain.hashtag.application.dto.response.HashTagResponse;
import com.seeat.server.domain.hashtag.application.usecase.HashTagUseCase;
import com.seeat.server.domain.hashtag.domain.entity.HashTag;
import com.seeat.server.domain.hashtag.domain.repository.HashTagRepository;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

@Service
@Transactional
Expand Down
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

리뷰와 해시태그 사이의 연결을 관리하는 서비스로서 create, load, delete, getByReview 등의 기능을 책임별로 잘 나눈 것 같습니다! 또한 해시태그 저장 시 각 파트별 1개 이상 필수 조건을 HashTagType 기준으로 검증한 로직은 비즈니스 로직 표현이 잘 된 것 같습니다!

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

감사합니다!!

Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
package com.seeat.server.domain.review.application.service;
package com.seeat.server.domain.hashtag.application.service;

import com.seeat.server.domain.review.application.dto.response.AuditoriumHashTagResponse;
import com.seeat.server.domain.review.application.usecase.ReviewHashTagUseCase;
import com.seeat.server.domain.review.domain.entity.HashTag;
import com.seeat.server.domain.review.domain.entity.HashTagType;
import com.seeat.server.domain.hashtag.application.dto.response.AuditoriumHashTagResponse;
import com.seeat.server.domain.hashtag.application.usecase.ReviewHashTagUseCase;
import com.seeat.server.domain.hashtag.domain.entity.HashTag;
import com.seeat.server.domain.hashtag.domain.entity.HashTagType;
import com.seeat.server.domain.review.domain.entity.Review;
import com.seeat.server.domain.review.domain.entity.ReviewHashTag;
import com.seeat.server.domain.review.domain.repository.HashTagRepository;
import com.seeat.server.domain.review.domain.repository.ReviewHashTagRepository;
import com.seeat.server.domain.review.domain.repository.dto.ReviewHashTagWithCount;
import com.seeat.server.domain.hashtag.domain.entity.ReviewHashTag;
import com.seeat.server.domain.hashtag.domain.repository.HashTagRepository;
import com.seeat.server.domain.hashtag.domain.repository.ReviewHashTagRepository;
import com.seeat.server.domain.hashtag.domain.repository.ReviewHashTagWithCount;
import com.seeat.server.domain.theater.application.usecase.TheaterUseCase;
import com.seeat.server.domain.theater.domain.entity.Auditorium;
import com.seeat.server.global.response.ErrorCode;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
package com.seeat.server.domain.review.application.usecase;
package com.seeat.server.domain.hashtag.application.usecase;

import com.seeat.server.domain.review.application.dto.response.HashTagResponse;
import com.seeat.server.domain.review.domain.entity.Review;
import com.seeat.server.domain.hashtag.application.dto.response.HashTagResponse;

import java.util.List;

Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
package com.seeat.server.domain.review.application.usecase;
package com.seeat.server.domain.hashtag.application.usecase;

import com.seeat.server.domain.review.application.dto.response.AuditoriumHashTagResponse;
import com.seeat.server.domain.hashtag.application.dto.response.AuditoriumHashTagResponse;
import com.seeat.server.domain.review.domain.entity.Review;
import com.seeat.server.domain.review.domain.entity.ReviewHashTag;
import com.seeat.server.domain.hashtag.domain.entity.ReviewHashTag;

import java.util.*;

Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package com.seeat.server.domain.review.domain.entity;
package com.seeat.server.domain.hashtag.domain.entity;

import com.seeat.server.domain.BaseEntity;
import jakarta.persistence.*;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package com.seeat.server.domain.review.domain.entity;
package com.seeat.server.domain.hashtag.domain.entity;

import lombok.Getter;
import lombok.RequiredArgsConstructor;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package com.seeat.server.domain.review.domain.entity;
package com.seeat.server.domain.hashtag.domain.entity;

import com.seeat.server.domain.BaseEntity;
import com.seeat.server.domain.review.domain.entity.Review;
import jakarta.persistence.*;
import lombok.AllArgsConstructor;
import lombok.Builder;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
package com.seeat.server.domain.review.domain.repository;
package com.seeat.server.domain.hashtag.domain.repository;

import com.seeat.server.domain.review.domain.entity.HashTag;
import com.seeat.server.domain.hashtag.domain.entity.HashTag;
import org.springframework.data.jpa.repository.JpaRepository;

import java.util.List;
Expand Down
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Auditorium → Seat → ReviewSeat → Review → ReviewHashTag로 전체 경로를 정확히 명시한 점이 아주 좋은 것 같습니다! DTO Projection 방식으로 성능적 이점도 챙겼습니다. 고생하셨습니다!

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

감사합니다!!

Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
package com.seeat.server.domain.review.domain.repository;
package com.seeat.server.domain.hashtag.domain.repository;

import com.seeat.server.domain.review.domain.entity.Review;
import com.seeat.server.domain.review.domain.entity.ReviewHashTag;
import com.seeat.server.domain.review.domain.repository.dto.ReviewHashTagWithCount;
import com.seeat.server.domain.hashtag.domain.entity.ReviewHashTag;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.query.Param;
Expand Down Expand Up @@ -34,11 +33,19 @@ public interface ReviewHashTagRepository extends JpaRepository<ReviewHashTag, Lo
* @param auditoriumId 상영관
* @return List<Review>
*/
@Query("SELECT rh.hashTag.id as hashTagId, rh.hashTag.name as hashTagName, COUNT(rh) AS count " +
"FROM Review r LEFT JOIN ReviewHashTag rh ON rh.review.id = r.id " +
"WHERE r.seat.auditorium.id = :auditoriumId " +
"GROUP BY rh.hashTag.id, rh.hashTag.name " +
"ORDER BY COUNT(rh) DESC, rh.hashTag.id DESC")
@Query("""
SELECT rh.hashTag.id as hashTagId,
rh.hashTag.name as hashTagName,
COUNT(DISTINCT r.id) AS count
FROM Auditorium a
LEFT JOIN Seat s ON s.auditorium = a
LEFT JOIN ReviewSeat rs ON rs.seat = s
LEFT JOIN Review r ON rs.review = r
LEFT JOIN ReviewHashTag rh ON rh.review = r
WHERE a.id = :auditoriumId
GROUP BY rh.hashTag.id, rh.hashTag.name
ORDER BY COUNT(DISTINCT r.id) DESC, rh.hashTag.id DESC
""")
List<ReviewHashTagWithCount> findByAuditorium_Id(@Param("auditoriumId") String auditoriumId);


Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package com.seeat.server.domain.review.domain.repository.dto;
package com.seeat.server.domain.hashtag.domain.repository;

public interface ReviewHashTagWithCount {

Expand Down
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
package com.seeat.server.domain.review.presentation;
package com.seeat.server.domain.hashtag.presentation;

import com.seeat.server.domain.review.application.dto.response.AuditoriumHashTagResponse;
import com.seeat.server.domain.review.application.dto.response.HashTagResponse;
import com.seeat.server.domain.review.application.usecase.HashTagUseCase;
import com.seeat.server.domain.review.application.usecase.ReviewHashTagUseCase;
import com.seeat.server.domain.review.presentation.swagger.HashTagControllerSpec;
import com.seeat.server.domain.hashtag.application.dto.response.AuditoriumHashTagResponse;
import com.seeat.server.domain.hashtag.application.dto.response.HashTagResponse;
import com.seeat.server.domain.hashtag.application.usecase.HashTagUseCase;
import com.seeat.server.domain.hashtag.application.usecase.ReviewHashTagUseCase;
import com.seeat.server.domain.hashtag.presentation.swagger.HashTagControllerSpec;
import com.seeat.server.global.response.ApiResponse;
import lombok.RequiredArgsConstructor;
import org.springframework.web.bind.annotation.GetMapping;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
package com.seeat.server.domain.review.presentation.swagger;
package com.seeat.server.domain.hashtag.presentation.swagger;

import com.seeat.server.domain.review.application.dto.response.AuditoriumHashTagResponse;
import com.seeat.server.domain.review.application.dto.response.HashTagResponse;
import com.seeat.server.domain.hashtag.application.dto.response.AuditoriumHashTagResponse;
import com.seeat.server.domain.hashtag.application.dto.response.HashTagResponse;
import com.seeat.server.global.response.ApiResponse;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,7 @@ public List<String> saveReviewImage(Review review, List<String> imageUrls) throw
public List<ReviewImage> getReviewImagesByReview(Review review) {

/// 리뷰에 해당하는 이미지 주소 순서대로 가져오기
List<ReviewImage> images = repository.findByReview(review);
List<ReviewImage> images = repository.findByReview_Id(review.getId());

/// 순서대로 정렬한 내용 출력
return images.stream()
Expand All @@ -96,13 +96,13 @@ public List<ReviewImage> getReviewImagesByReview(Review review) {
/// 삭제하기
/**
* 리뷰에 존재하는 모든 이미지 삭제
* @param review 리뷰
* @param reviewId 리뷰
*/
@Override
public void deleteReviewImage(Review review) throws IOException {
public void deleteReviewImage(Long reviewId) throws IOException {

/// 리뷰에 해당하는 파일 이름 다 가져오기
List<ReviewImage> images = repository.findByReview(review);
List<ReviewImage> images = repository.findByReview_Id(reviewId);

/// 파일 이름만 추출하기
List<String> imagesList = images.stream()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ public interface ReviewImageUseCase {
List<ReviewImage> getReviewImagesByReview(Review review);

/// 삭제
void deleteReviewImage(Review review) throws IOException;
void deleteReviewImage(Long reviewId) throws IOException;



Expand Down
Original file line number Diff line number Diff line change
@@ -1,13 +1,12 @@
package com.seeat.server.domain.image.domain.repository;

import com.seeat.server.domain.review.domain.entity.Review;
import com.seeat.server.domain.image.domain.entity.ReviewImage;
import org.springframework.data.jpa.repository.JpaRepository;

import java.util.List;

public interface ReviewImageRepository extends JpaRepository<ReviewImage, Long> {
List<ReviewImage> findByReview(Review review);
List<ReviewImage> findByReview_Id(Long reviewId);

void deleteAllByImageUrlIn(List<String> imageUrls);
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@

import com.seeat.server.domain.manage.domain.entity.Feedback;
import com.seeat.server.domain.user.application.dto.response.UserResponse;
import io.swagger.v3.oas.annotations.media.Schema;
import io.swagger.v3.oas.annotations.tags.Tag;
import lombok.Builder;


Expand All @@ -14,7 +16,10 @@


@Builder
@Schema(name = "[응답][피드백] 피드백 상세 조회 Response",description = "피드백 상세 조회에 대한 DTO 입니다.")
public record FeedbackDetailResponse(

@Schema(description = "피드백 내용", example = "메가박스 강남에 대한 정보가 틀린것 같아요!")
String content,
UserResponse user
) {
Expand Down
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Review, Seats, Images, HashTags 등의 도메인 데이터를 명확히 분리한 것이 포인트이고, seats.get(0)을 기준으로 상영관 정보를 추출한 판단은 도메인 룰(모든 좌석은 같은 상영관)에 기반해 설계된 것으로 적절한 것 같습니다!

Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
package com.seeat.server.domain.review.application.dto.response;

import com.seeat.server.domain.hashtag.application.dto.response.ReviewHashTagResponse;
import com.seeat.server.domain.review.domain.entity.Review;
import com.seeat.server.domain.review.domain.entity.ReviewHashTag;
import com.seeat.server.domain.hashtag.domain.entity.ReviewHashTag;
import com.seeat.server.domain.image.domain.entity.ReviewImage;
import com.seeat.server.domain.theater.domain.entity.Seat;
import com.seeat.server.domain.user.application.dto.response.UserResponse;
Expand All @@ -10,7 +11,6 @@
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Builder;

import java.time.LocalDateTime;
import java.util.List;

/**
Expand Down Expand Up @@ -67,8 +67,8 @@ public static ReviewDetailResponse from(
List<ReviewHashTag> hashTags, Long heartCount, List<ReviewImage> images, List<Seat> seats
) {

/// 좌석 정보
Seat seat = review.getSeat();
/// 같은 상영관의 좌석이기에,
Seat seat = seats.get(0);

return ReviewDetailResponse.builder()
.movieTitle(review.getMovieTitle())
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
package com.seeat.server.domain.review.application.dto.response;

import com.seeat.server.domain.hashtag.application.dto.response.ReviewHashTagResponse;
import com.seeat.server.domain.review.domain.entity.Review;
import com.seeat.server.domain.review.domain.entity.ReviewHashTag;
import com.seeat.server.domain.hashtag.domain.entity.ReviewHashTag;
import com.seeat.server.domain.user.application.dto.response.UserResponse;
import com.seeat.server.global.util.DateFormatUtil;
import io.swagger.v3.oas.annotations.media.Schema;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
package com.seeat.server.domain.review.application.dto.response;

import com.seeat.server.domain.review.domain.entity.Review;
import lombok.Builder;
import java.util.List;

@Builder
public record ReviewSaveResponse(Long reviewId) {


/// 정적 팩토리 메서드
public static ReviewSaveResponse from(Review review) {
return ReviewSaveResponse.builder()
.reviewId(review.getId())
.build();
}

/// 정적 팩토리 메서드
public static List<ReviewSaveResponse> from(List<Review> reviews){
return reviews.stream()
.map(ReviewSaveResponse::from)
.toList();
}

}
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
package com.seeat.server.domain.review.application.dto.response;

import com.seeat.server.domain.review.domain.repository.dto.SeatReviewStats;
import com.seeat.server.domain.review.domain.repository.dto.ReviewSeatStats;
import com.seeat.server.domain.theater.domain.entity.Seat;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Builder;
Expand Down Expand Up @@ -38,7 +38,7 @@ public record ReviewSeatListResponse(
) {

/// 정적 팩토리 메서드
public static ReviewSeatListResponse from(SeatReviewStats stats, List<ReviewListResponse> reviews) {
public static ReviewSeatListResponse from(ReviewSeatStats stats, List<ReviewListResponse> reviews) {

Seat seat = stats.getSeat();

Expand Down
Loading
Loading