diff --git a/build.gradle b/build.gradle index c7cbe6f..66fcdf5 100644 --- a/build.gradle +++ b/build.gradle @@ -2,6 +2,7 @@ plugins { id 'java' id 'org.springframework.boot' version '3.3.3' id 'io.spring.dependency-management' version '1.1.6' + id 'com.ewerk.gradle.plugins.querydsl' version '1.0.10' } allprojects { diff --git a/core/src/main/java/com/pocket/core/config/QueryDslConfig.java b/core/src/main/java/com/pocket/core/config/QueryDslConfig.java new file mode 100644 index 0000000..ad007e8 --- /dev/null +++ b/core/src/main/java/com/pocket/core/config/QueryDslConfig.java @@ -0,0 +1,19 @@ +package com.pocket.core.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); + } +} diff --git a/core/src/main/java/com/pocket/core/exception/review/ReviewCustomException.java b/core/src/main/java/com/pocket/core/exception/review/ReviewCustomException.java new file mode 100644 index 0000000..a5af63f --- /dev/null +++ b/core/src/main/java/com/pocket/core/exception/review/ReviewCustomException.java @@ -0,0 +1,17 @@ +package com.pocket.core.exception.review; + + +import lombok.Getter; +import org.springframework.http.HttpStatus; +import org.springframework.web.bind.annotation.ResponseStatus; + +@Getter +@ResponseStatus(HttpStatus.BAD_REQUEST) +public class ReviewCustomException extends RuntimeException { + + private final ReviewErrorCode errorCode; + + public ReviewCustomException(ReviewErrorCode errorCode) { + this.errorCode = errorCode; + } +} diff --git a/core/src/main/java/com/pocket/core/exception/review/ReviewErrorCode.java b/core/src/main/java/com/pocket/core/exception/review/ReviewErrorCode.java new file mode 100644 index 0000000..38f85bb --- /dev/null +++ b/core/src/main/java/com/pocket/core/exception/review/ReviewErrorCode.java @@ -0,0 +1,24 @@ +package com.pocket.core.exception.review; + +import com.pocket.core.exception.common.ApiResponse; +import com.pocket.core.exception.common.BaseErrorCode; +import lombok.AllArgsConstructor; +import lombok.Getter; +import org.springframework.http.HttpStatus; + +@Getter +@AllArgsConstructor +public enum ReviewErrorCode implements BaseErrorCode { + PHOTO_FEATURE_NOT_FOUND(HttpStatus.BAD_REQUEST, "400","해당 설명에 맞는 PhotoFeature가 없습니다"), + BOOTH_FEATURE_NOT_FOUND(HttpStatus.BAD_REQUEST, "400","해당 설명에 맞는 BoothFeature가 없습니다"); + + + private final HttpStatus httpStatus; + private final String code; + private final String message; + + @Override + public ApiResponse getErrorResponse() { + return ApiResponse.onFailure(code, message); + } +} diff --git a/domain/src/main/java/com/pocket/domain/dto/review/ReviewBoothFeatureDto.java b/domain/src/main/java/com/pocket/domain/dto/review/ReviewBoothFeatureDto.java new file mode 100644 index 0000000..feaba42 --- /dev/null +++ b/domain/src/main/java/com/pocket/domain/dto/review/ReviewBoothFeatureDto.java @@ -0,0 +1,9 @@ +package com.pocket.domain.dto.review; + +import java.util.List; + +public record ReviewBoothFeatureDto( + List featureName, + int count +) { +} diff --git a/domain/src/main/java/com/pocket/domain/dto/review/ReviewGet6ImagesResponseDto.java b/domain/src/main/java/com/pocket/domain/dto/review/ReviewGet6ImagesResponseDto.java new file mode 100644 index 0000000..82c9c09 --- /dev/null +++ b/domain/src/main/java/com/pocket/domain/dto/review/ReviewGet6ImagesResponseDto.java @@ -0,0 +1,9 @@ +package com.pocket.domain.dto.review; + +import java.util.List; + +public record ReviewGet6ImagesResponseDto( + List filePaths, + int totalImageCount +) { +} diff --git a/domain/src/main/java/com/pocket/domain/dto/review/ReviewGetRecentResponseDto.java b/domain/src/main/java/com/pocket/domain/dto/review/ReviewGetRecentResponseDto.java new file mode 100644 index 0000000..bf0ec80 --- /dev/null +++ b/domain/src/main/java/com/pocket/domain/dto/review/ReviewGetRecentResponseDto.java @@ -0,0 +1,9 @@ +package com.pocket.domain.dto.review; + +import java.util.List; + +public record ReviewGetRecentResponseDto( + int reviewCount, + List reviews +) { +} diff --git a/domain/src/main/java/com/pocket/domain/dto/review/ReviewPhotoFeatureDto.java b/domain/src/main/java/com/pocket/domain/dto/review/ReviewPhotoFeatureDto.java new file mode 100644 index 0000000..08d520f --- /dev/null +++ b/domain/src/main/java/com/pocket/domain/dto/review/ReviewPhotoFeatureDto.java @@ -0,0 +1,9 @@ +package com.pocket.domain.dto.review; + +import java.util.List; + +public record ReviewPhotoFeatureDto( + List featureName, + int count +) { +} diff --git a/domain/src/main/java/com/pocket/domain/dto/review/ReviewPreviewDto.java b/domain/src/main/java/com/pocket/domain/dto/review/ReviewPreviewDto.java new file mode 100644 index 0000000..d090bf2 --- /dev/null +++ b/domain/src/main/java/com/pocket/domain/dto/review/ReviewPreviewDto.java @@ -0,0 +1,16 @@ +package com.pocket.domain.dto.review; + +import java.util.List; + +public record ReviewPreviewDto( + Long photoboothId, + String name, + String year, + String month, + String date, + String contents, + List features, + String imageUrl, + int imageCount +) { +} diff --git a/domain/src/main/java/com/pocket/domain/dto/review/ReviewRegisterRequestDto.java b/domain/src/main/java/com/pocket/domain/dto/review/ReviewRegisterRequestDto.java new file mode 100644 index 0000000..6e9a4ab --- /dev/null +++ b/domain/src/main/java/com/pocket/domain/dto/review/ReviewRegisterRequestDto.java @@ -0,0 +1,14 @@ +package com.pocket.domain.dto.review; + +import java.util.List; + +public record ReviewRegisterRequestDto( + Long photoboothId, + int rating, + List boothFeatures, + List photoFeatures, + List filePaths, + String content + ) { + +} diff --git a/domain/src/main/java/com/pocket/domain/dto/review/ReviewRegisterResponseDto.java b/domain/src/main/java/com/pocket/domain/dto/review/ReviewRegisterResponseDto.java new file mode 100644 index 0000000..baaa63d --- /dev/null +++ b/domain/src/main/java/com/pocket/domain/dto/review/ReviewRegisterResponseDto.java @@ -0,0 +1,14 @@ +package com.pocket.domain.dto.review; + +import java.util.List; + +public record ReviewRegisterResponseDto( + Long photoboothId, + int rating, + List boothFeatures, + List photoFeatures, + List filePaths, + String content +) { + +} diff --git a/domain/src/main/java/com/pocket/domain/entity/image/Image.java b/domain/src/main/java/com/pocket/domain/entity/image/Image.java index 31140e3..8c6b94b 100644 --- a/domain/src/main/java/com/pocket/domain/entity/image/Image.java +++ b/domain/src/main/java/com/pocket/domain/entity/image/Image.java @@ -1,6 +1,7 @@ package com.pocket.domain.entity.image; import com.pocket.domain.dto.album.AlbumRegisterRequestDto; +import com.pocket.domain.dto.review.ReviewRegisterRequestDto; import com.pocket.domain.entity.BaseEntity; import jakarta.persistence.Column; import jakarta.persistence.Embeddable; @@ -30,11 +31,16 @@ public Image(ImageType type) { this.type = type; } - public void makeImage(AlbumRegisterRequestDto dto, String filePath) { + public void makeAlbumImage(AlbumRegisterRequestDto dto, String filePath) { this.type = ImageType.PHOTO; this.imageUrl = filePath; this.year = dto.year(); this.month = dto.month(); this.date = dto.date(); } + + public void makeReviewImage(String filePath) { + this.type = ImageType.REVIEW; + this.imageUrl = filePath; + } } \ No newline at end of file diff --git a/domain/src/main/java/com/pocket/domain/entity/photobooth/PhotoBooth.java b/domain/src/main/java/com/pocket/domain/entity/photobooth/PhotoBooth.java index d440ee1..0351393 100644 --- a/domain/src/main/java/com/pocket/domain/entity/photobooth/PhotoBooth.java +++ b/domain/src/main/java/com/pocket/domain/entity/photobooth/PhotoBooth.java @@ -6,6 +6,8 @@ import lombok.Getter; import lombok.NoArgsConstructor; +import java.math.BigDecimal; + @Getter @Embeddable @NoArgsConstructor(access = AccessLevel.PROTECTED) @@ -26,16 +28,33 @@ public class PhotoBooth { @Column(name = "photo_booth_brand") private PhotoBoothBrand photoBoothBrand; + // 총 리뷰 수 + private int totalReviews; + + // 평균 별점 + @Column(precision = 3, scale = 2) + private BigDecimal averageRating; // 기본값을 0으로 설정 + private PhotoBooth(String name, String road, Double x, Double y, PhotoBoothBrand photoBoothBrand) { this.name = name; this.road = road; this.x = x; this.y = y; this.photoBoothBrand = photoBoothBrand; + this.totalReviews = 0; + this.averageRating = BigDecimal.ZERO; } // 정적 팩토리 메서드 public static PhotoBooth create(String name, String road, Double x, Double y, PhotoBoothBrand brand) { return new PhotoBooth(name, road, x, y, brand); } + + public void updateRating(int newRating) { + this.totalReviews += 1; + BigDecimal totalScore = this.averageRating.multiply(BigDecimal.valueOf(this.totalReviews - 1)) + .add(BigDecimal.valueOf(newRating)); + this.averageRating = totalScore.divide(BigDecimal.valueOf(this.totalReviews), 2, BigDecimal.ROUND_HALF_UP); + } + } diff --git a/domain/src/main/java/com/pocket/domain/entity/review/BoothFeature.java b/domain/src/main/java/com/pocket/domain/entity/review/BoothFeature.java index 63c4dfd..d7eb19e 100644 --- a/domain/src/main/java/com/pocket/domain/entity/review/BoothFeature.java +++ b/domain/src/main/java/com/pocket/domain/entity/review/BoothFeature.java @@ -1,6 +1,9 @@ package com.pocket.domain.entity.review; +import com.pocket.core.exception.review.ReviewCustomException; +import com.pocket.core.exception.review.ReviewErrorCode; + public enum BoothFeature { CLEAN_PROPS("깔끔한 소품"), PRETTY_SELFIE_ZONE("예쁜 셀카존"), @@ -21,4 +24,14 @@ public enum BoothFeature { public String getDescription() { return description; } + + // 한글 설명을 받아 enum을 반환하는 메소드 + public static BoothFeature fromDescription(String description) { + for (BoothFeature feature : BoothFeature.values()) { + if (feature.getDescription().equals(description)) { + return feature; + } + } + throw new ReviewCustomException(ReviewErrorCode.BOOTH_FEATURE_NOT_FOUND); + } } \ No newline at end of file diff --git a/domain/src/main/java/com/pocket/domain/entity/review/PhotoFeature.java b/domain/src/main/java/com/pocket/domain/entity/review/PhotoFeature.java index e4966aa..e240ab1 100644 --- a/domain/src/main/java/com/pocket/domain/entity/review/PhotoFeature.java +++ b/domain/src/main/java/com/pocket/domain/entity/review/PhotoFeature.java @@ -1,5 +1,8 @@ package com.pocket.domain.entity.review; +import com.pocket.core.exception.review.ReviewCustomException; +import com.pocket.core.exception.review.ReviewErrorCode; + public enum PhotoFeature { NO_GLARE("빛번짐 없음"), HIGH_DEFINITION("선명한 화질"), @@ -18,4 +21,14 @@ public enum PhotoFeature { public String getDescription() { return description; } + + // 한글 설명을 받아 enum을 반환하는 메소드 + public static PhotoFeature fromDescription(String description) { + for (PhotoFeature feature : PhotoFeature.values()) { + if (feature.getDescription().equals(description)) { + return feature; + } + } + throw new ReviewCustomException(ReviewErrorCode.PHOTO_FEATURE_NOT_FOUND); + } } diff --git a/domain/src/main/java/com/pocket/domain/entity/review/Review.java b/domain/src/main/java/com/pocket/domain/entity/review/Review.java index a5226a5..34479da 100644 --- a/domain/src/main/java/com/pocket/domain/entity/review/Review.java +++ b/domain/src/main/java/com/pocket/domain/entity/review/Review.java @@ -1,16 +1,16 @@ package com.pocket.domain.entity.review; +import com.pocket.domain.entity.BaseEntity; import jakarta.persistence.*; -import lombok.AccessLevel; -import lombok.Getter; -import lombok.NoArgsConstructor; +import lombok.*; import java.util.List; @Getter @Embeddable @NoArgsConstructor(access = AccessLevel.PROTECTED) -public class Review { +@AllArgsConstructor +public class Review extends BaseEntity { @Column(name = "rating") private int rating; @@ -18,4 +18,17 @@ public class Review { @Column(name = "content") private String content; + @ElementCollection(targetClass = BoothFeature.class) + @Enumerated(EnumType.STRING) + @Column(name = "booth_features") + private List boothFeatures; + + @ElementCollection(targetClass = PhotoFeature.class) + @Enumerated(EnumType.STRING) + @Column(name = "photo_features") + private List photoFeatures; + + + + } diff --git a/domain/src/main/java/com/pocket/domain/port/photobooth/PhotoBoothGetNamePort.java b/domain/src/main/java/com/pocket/domain/port/photobooth/PhotoBoothGetNamePort.java new file mode 100644 index 0000000..870233f --- /dev/null +++ b/domain/src/main/java/com/pocket/domain/port/photobooth/PhotoBoothGetNamePort.java @@ -0,0 +1,7 @@ +package com.pocket.domain.port.photobooth; + +public interface PhotoBoothGetNamePort { + + String getPhotoBoothName(Long photoboothId); + +} diff --git a/domain/src/main/java/com/pocket/domain/port/photobooth/PhotoBoothGetRatingPort.java b/domain/src/main/java/com/pocket/domain/port/photobooth/PhotoBoothGetRatingPort.java new file mode 100644 index 0000000..4a197c9 --- /dev/null +++ b/domain/src/main/java/com/pocket/domain/port/photobooth/PhotoBoothGetRatingPort.java @@ -0,0 +1,9 @@ +package com.pocket.domain.port.photobooth; + +import java.math.BigDecimal; + +public interface PhotoBoothGetRatingPort { + + BigDecimal getRating(Long photoboothId); + +} diff --git a/domain/src/main/java/com/pocket/domain/port/review/ReviewBoothFeaturePort.java b/domain/src/main/java/com/pocket/domain/port/review/ReviewBoothFeaturePort.java new file mode 100644 index 0000000..35cc042 --- /dev/null +++ b/domain/src/main/java/com/pocket/domain/port/review/ReviewBoothFeaturePort.java @@ -0,0 +1,12 @@ +package com.pocket.domain.port.review; + + +import com.pocket.domain.dto.review.ReviewBoothFeatureDto; + +import java.util.List; + +public interface ReviewBoothFeaturePort { + + List getReviewBoothFeature(Long photoboothId); + +} diff --git a/domain/src/main/java/com/pocket/domain/port/review/ReviewGet6ImagesPort.java b/domain/src/main/java/com/pocket/domain/port/review/ReviewGet6ImagesPort.java new file mode 100644 index 0000000..1ee0bdb --- /dev/null +++ b/domain/src/main/java/com/pocket/domain/port/review/ReviewGet6ImagesPort.java @@ -0,0 +1,9 @@ +package com.pocket.domain.port.review; + +import com.pocket.domain.dto.review.ReviewGet6ImagesResponseDto; + +public interface ReviewGet6ImagesPort { + + ReviewGet6ImagesResponseDto get6Images(Long photoboothId); + +} diff --git a/domain/src/main/java/com/pocket/domain/port/review/ReviewGetAllImagesPort.java b/domain/src/main/java/com/pocket/domain/port/review/ReviewGetAllImagesPort.java new file mode 100644 index 0000000..13ad7f0 --- /dev/null +++ b/domain/src/main/java/com/pocket/domain/port/review/ReviewGetAllImagesPort.java @@ -0,0 +1,9 @@ +package com.pocket.domain.port.review; + +import java.util.List; + +public interface ReviewGetAllImagesPort { + + List getAllImages(Long photoboothId); + +} diff --git a/domain/src/main/java/com/pocket/domain/port/review/ReviewGetRecentPort.java b/domain/src/main/java/com/pocket/domain/port/review/ReviewGetRecentPort.java new file mode 100644 index 0000000..68d797b --- /dev/null +++ b/domain/src/main/java/com/pocket/domain/port/review/ReviewGetRecentPort.java @@ -0,0 +1,9 @@ +package com.pocket.domain.port.review; + +import com.pocket.domain.dto.review.ReviewGetRecentResponseDto; + +public interface ReviewGetRecentPort { + + ReviewGetRecentResponseDto getRecentReview(Long photoboothId); + +} diff --git a/domain/src/main/java/com/pocket/domain/port/review/ReviewPhotoFeaturePort.java b/domain/src/main/java/com/pocket/domain/port/review/ReviewPhotoFeaturePort.java new file mode 100644 index 0000000..9f7e4de --- /dev/null +++ b/domain/src/main/java/com/pocket/domain/port/review/ReviewPhotoFeaturePort.java @@ -0,0 +1,12 @@ +package com.pocket.domain.port.review; + + +import com.pocket.domain.dto.review.ReviewPhotoFeatureDto; + +import java.util.List; + +public interface ReviewPhotoFeaturePort { + + List getReviewPhotoFeature(Long photoboothId); + +} diff --git a/domain/src/main/java/com/pocket/domain/port/review/ReviewRegisterPort.java b/domain/src/main/java/com/pocket/domain/port/review/ReviewRegisterPort.java new file mode 100644 index 0000000..02453b9 --- /dev/null +++ b/domain/src/main/java/com/pocket/domain/port/review/ReviewRegisterPort.java @@ -0,0 +1,10 @@ +package com.pocket.domain.port.review; + +import com.pocket.domain.dto.review.ReviewRegisterRequestDto; +import com.pocket.domain.dto.review.ReviewRegisterResponseDto; + +public interface ReviewRegisterPort { + + ReviewRegisterResponseDto registerReview(ReviewRegisterRequestDto requestDto, String name); + +} diff --git a/domain/src/main/java/com/pocket/domain/service/photobooth/PhotoBoothFindService.java b/domain/src/main/java/com/pocket/domain/service/photobooth/PhotoBoothService.java similarity index 51% rename from domain/src/main/java/com/pocket/domain/service/photobooth/PhotoBoothFindService.java rename to domain/src/main/java/com/pocket/domain/service/photobooth/PhotoBoothService.java index 4b96f80..b9ae787 100644 --- a/domain/src/main/java/com/pocket/domain/service/photobooth/PhotoBoothFindService.java +++ b/domain/src/main/java/com/pocket/domain/service/photobooth/PhotoBoothService.java @@ -4,24 +4,38 @@ import com.pocket.domain.dto.photobooth.PhotoBoothFindResponseDto; import com.pocket.domain.entity.photobooth.PhotoBoothBrand; import com.pocket.domain.port.photobooth.PhotoBoothFindPort; +import com.pocket.domain.port.photobooth.PhotoBoothGetNamePort; +import com.pocket.domain.port.photobooth.PhotoBoothGetRatingPort; import com.pocket.domain.usecase.photobooth.PhotoBoothFindUseCase; +import com.pocket.domain.usecase.photobooth.PhotoBoothGetNameUseCase; +import com.pocket.domain.usecase.photobooth.PhotoBoothGetRatingUseCase; import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Service; +import java.math.BigDecimal; import java.util.List; @Service @RequiredArgsConstructor -public class PhotoBoothFindService implements PhotoBoothFindUseCase { +public class PhotoBoothService implements PhotoBoothFindUseCase, PhotoBoothGetNameUseCase, PhotoBoothGetRatingUseCase { private final PhotoBoothFindPort photoBoothFindPort; + private final PhotoBoothGetRatingPort photoBoothGetRatingPort; + private final PhotoBoothGetNamePort photoBoothGetNamePort; public PhotoBoothFindResponseDto findPhotoBoothResponse(Long id) { return photoBoothFindPort.findById(id); } + public String getPhotoBoothName(Long photoboothId) { + return photoBoothGetNamePort.getPhotoBoothName(photoboothId); + } public List findNearPhotoBooth(double lat, double lon, List brand) { return photoBoothFindPort.getPhotoboothWithin2Km(lat, lon, brand); } + + public BigDecimal getPhotoBoothRating(Long photoboothId) { + return photoBoothGetRatingPort.getRating(photoboothId); + } } diff --git a/domain/src/main/java/com/pocket/domain/service/review/ReviewService.java b/domain/src/main/java/com/pocket/domain/service/review/ReviewService.java new file mode 100644 index 0000000..44fca27 --- /dev/null +++ b/domain/src/main/java/com/pocket/domain/service/review/ReviewService.java @@ -0,0 +1,55 @@ +package com.pocket.domain.service.review; + + +import com.pocket.core.aop.annotation.DomainService; +import com.pocket.domain.dto.review.*; +import com.pocket.domain.port.review.*; +import com.pocket.domain.usecase.review.*; +import lombok.RequiredArgsConstructor; + +import java.util.List; + +@DomainService +@RequiredArgsConstructor +public class ReviewService implements ReviewRegisterUseCase, ReviewGet6ImagesUseCase, ReviewGetRecentUseCase, ReviewGetAllImagesUseCase, ReviewBoothFeatureUseCase, ReviewPhotoFeatureUseCase + +{ + + private final ReviewRegisterPort reviewRegisterPort; + private final ReviewGet6ImagesPort reviewGet6ImagesPort; + private final ReviewGetRecentPort reviewGetRecentPort; + private final ReviewGetAllImagesPort reviewGetAllImagesPort; + private final ReviewBoothFeaturePort reviewBoothFeaturePort; + private final ReviewPhotoFeaturePort reviewPhotoFeaturePort; + + @Override + public ReviewRegisterResponseDto registerReviewResponse(ReviewRegisterRequestDto reviewRegisterRequestDto, String name) { + return reviewRegisterPort.registerReview(reviewRegisterRequestDto, name); + } + + + @Override + public ReviewGet6ImagesResponseDto get6Images(Long photoboothId) { + return reviewGet6ImagesPort.get6Images(photoboothId); + } + + @Override + public ReviewGetRecentResponseDto getRecentReview(Long photoboothId) { + return reviewGetRecentPort.getRecentReview(photoboothId); + } + + @Override + public List getAllImages(Long photoboothId) { + return reviewGetAllImagesPort.getAllImages(photoboothId); + } + + @Override + public List getReviewBoothFeatures(Long photoboothId) { + return reviewBoothFeaturePort.getReviewBoothFeature(photoboothId); + } + + @Override + public List getReviewPhotoFeatures(Long photoboothId) { + return reviewPhotoFeaturePort.getReviewPhotoFeature(photoboothId); + } +} diff --git a/domain/src/main/java/com/pocket/domain/usecase/photobooth/PhotoBoothGetNameUseCase.java b/domain/src/main/java/com/pocket/domain/usecase/photobooth/PhotoBoothGetNameUseCase.java new file mode 100644 index 0000000..9720d01 --- /dev/null +++ b/domain/src/main/java/com/pocket/domain/usecase/photobooth/PhotoBoothGetNameUseCase.java @@ -0,0 +1,8 @@ +package com.pocket.domain.usecase.photobooth; + +public interface PhotoBoothGetNameUseCase { + + String getPhotoBoothName(Long photoboothId); + + +} diff --git a/domain/src/main/java/com/pocket/domain/usecase/photobooth/PhotoBoothGetRatingUseCase.java b/domain/src/main/java/com/pocket/domain/usecase/photobooth/PhotoBoothGetRatingUseCase.java new file mode 100644 index 0000000..ed44d33 --- /dev/null +++ b/domain/src/main/java/com/pocket/domain/usecase/photobooth/PhotoBoothGetRatingUseCase.java @@ -0,0 +1,9 @@ +package com.pocket.domain.usecase.photobooth; + +import java.math.BigDecimal; + +public interface PhotoBoothGetRatingUseCase { + + BigDecimal getPhotoBoothRating(Long photoboothId); + +} diff --git a/domain/src/main/java/com/pocket/domain/usecase/review/ReviewBoothFeatureUseCase.java b/domain/src/main/java/com/pocket/domain/usecase/review/ReviewBoothFeatureUseCase.java new file mode 100644 index 0000000..1d31f79 --- /dev/null +++ b/domain/src/main/java/com/pocket/domain/usecase/review/ReviewBoothFeatureUseCase.java @@ -0,0 +1,11 @@ +package com.pocket.domain.usecase.review; + +import com.pocket.domain.dto.review.ReviewBoothFeatureDto; + +import java.util.List; + +public interface ReviewBoothFeatureUseCase { + + List getReviewBoothFeatures(Long photoboothId); + +} diff --git a/domain/src/main/java/com/pocket/domain/usecase/review/ReviewGet6ImagesUseCase.java b/domain/src/main/java/com/pocket/domain/usecase/review/ReviewGet6ImagesUseCase.java new file mode 100644 index 0000000..31ddcb5 --- /dev/null +++ b/domain/src/main/java/com/pocket/domain/usecase/review/ReviewGet6ImagesUseCase.java @@ -0,0 +1,10 @@ +package com.pocket.domain.usecase.review; + + +import com.pocket.domain.dto.review.ReviewGet6ImagesResponseDto; + +public interface ReviewGet6ImagesUseCase { + + ReviewGet6ImagesResponseDto get6Images(Long photoboothId); + +} diff --git a/domain/src/main/java/com/pocket/domain/usecase/review/ReviewGetAllImagesUseCase.java b/domain/src/main/java/com/pocket/domain/usecase/review/ReviewGetAllImagesUseCase.java new file mode 100644 index 0000000..2c60a90 --- /dev/null +++ b/domain/src/main/java/com/pocket/domain/usecase/review/ReviewGetAllImagesUseCase.java @@ -0,0 +1,9 @@ +package com.pocket.domain.usecase.review; + +import java.util.List; + +public interface ReviewGetAllImagesUseCase { + + List getAllImages(Long photoboothId); + +} diff --git a/domain/src/main/java/com/pocket/domain/usecase/review/ReviewGetRecentUseCase.java b/domain/src/main/java/com/pocket/domain/usecase/review/ReviewGetRecentUseCase.java new file mode 100644 index 0000000..6d0d2ce --- /dev/null +++ b/domain/src/main/java/com/pocket/domain/usecase/review/ReviewGetRecentUseCase.java @@ -0,0 +1,10 @@ +package com.pocket.domain.usecase.review; + +import com.pocket.domain.dto.review.ReviewGetRecentResponseDto; + + +public interface ReviewGetRecentUseCase { + + ReviewGetRecentResponseDto getRecentReview(Long photoboohtId); + +} diff --git a/domain/src/main/java/com/pocket/domain/usecase/review/ReviewPhotoFeatureUseCase.java b/domain/src/main/java/com/pocket/domain/usecase/review/ReviewPhotoFeatureUseCase.java new file mode 100644 index 0000000..0d862d5 --- /dev/null +++ b/domain/src/main/java/com/pocket/domain/usecase/review/ReviewPhotoFeatureUseCase.java @@ -0,0 +1,12 @@ +package com.pocket.domain.usecase.review; + +import com.pocket.domain.dto.review.ReviewPhotoFeatureDto; + +import java.util.List; + + +public interface ReviewPhotoFeatureUseCase { + + List getReviewPhotoFeatures(Long photoboothId); + +} diff --git a/domain/src/main/java/com/pocket/domain/usecase/review/ReviewRegisterUseCase.java b/domain/src/main/java/com/pocket/domain/usecase/review/ReviewRegisterUseCase.java new file mode 100644 index 0000000..79c3856 --- /dev/null +++ b/domain/src/main/java/com/pocket/domain/usecase/review/ReviewRegisterUseCase.java @@ -0,0 +1,10 @@ +package com.pocket.domain.usecase.review; + +import com.pocket.domain.dto.review.ReviewRegisterRequestDto; +import com.pocket.domain.dto.review.ReviewRegisterResponseDto; + +public interface ReviewRegisterUseCase { + + ReviewRegisterResponseDto registerReviewResponse(ReviewRegisterRequestDto requestDto, String name); + +} diff --git a/inbounds/src/main/java/com/pocket/inbounds/album/presentation/AlbumContollerDocs.java b/inbounds/src/main/java/com/pocket/inbounds/album/presentation/AlbumContollerDocs.java index 5f892aa..bb2d9ef 100644 --- a/inbounds/src/main/java/com/pocket/inbounds/album/presentation/AlbumContollerDocs.java +++ b/inbounds/src/main/java/com/pocket/inbounds/album/presentation/AlbumContollerDocs.java @@ -1,7 +1,31 @@ package com.pocket.inbounds.album.presentation; +import com.nimbusds.oauth2.sdk.ErrorResponse; +import com.pocket.core.exception.common.ApplicationResponse; +import com.pocket.domain.dto.album.AlbumRegisterRequestDto; +import com.pocket.domain.dto.album.AlbumRegisterResponseDto; +import com.pocket.domain.dto.user.UserInfoDTO; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.media.Content; +import io.swagger.v3.oas.annotations.media.Schema; +import io.swagger.v3.oas.annotations.responses.ApiResponse; +import io.swagger.v3.oas.annotations.responses.ApiResponses; import io.swagger.v3.oas.annotations.tags.Tag; +import org.springframework.security.core.annotation.AuthenticationPrincipal; +import org.springframework.web.bind.annotation.RequestBody; @Tag(name = "Album", description = "Album API") public interface AlbumContollerDocs { + + @ApiResponses(value = { + @ApiResponse(responseCode = "200", description = "OK"), + @ApiResponse(responseCode = "400", description = "BAD REQUEST", + content = {@Content(schema = @Schema(implementation = ErrorResponse.class))}), + @ApiResponse(responseCode = "500", description = "INTERNAL SERVER ERROR", + content = {@Content(schema = @Schema(implementation = ErrorResponse.class))}) + }) + @Operation(summary = "사진 앨범에 사진 등록", description = "사용자가 사진 앨범에 사진을 등록하는 API") + ApplicationResponse postPhoto( + @RequestBody AlbumRegisterRequestDto requestDto, + @AuthenticationPrincipal UserInfoDTO user); } diff --git a/inbounds/src/main/java/com/pocket/inbounds/album/presentation/AlbumController.java b/inbounds/src/main/java/com/pocket/inbounds/album/presentation/AlbumController.java index 080cfa4..b286d3e 100644 --- a/inbounds/src/main/java/com/pocket/inbounds/album/presentation/AlbumController.java +++ b/inbounds/src/main/java/com/pocket/inbounds/album/presentation/AlbumController.java @@ -24,8 +24,8 @@ public ApplicationResponse postPhoto( @RequestBody AlbumRegisterRequestDto requestDto, @AuthenticationPrincipal UserInfoDTO user) { - AlbumRegisterResponseDto url = albumRegisterUseCase.registerPhotoResponse(requestDto, user.name()); - return ApplicationResponse.ok(url); // presigned Url을 리턴해줘야 함. + AlbumRegisterResponseDto response = albumRegisterUseCase.registerPhotoResponse(requestDto, user.name()); + return ApplicationResponse.ok(response); } } diff --git a/inbounds/src/main/java/com/pocket/inbounds/file/presentation/FileControllerDocs.java b/inbounds/src/main/java/com/pocket/inbounds/file/presentation/FileControllerDocs.java index d963323..88e7221 100644 --- a/inbounds/src/main/java/com/pocket/inbounds/file/presentation/FileControllerDocs.java +++ b/inbounds/src/main/java/com/pocket/inbounds/file/presentation/FileControllerDocs.java @@ -1,7 +1,29 @@ package com.pocket.inbounds.file.presentation; +import com.nimbusds.oauth2.sdk.ErrorResponse; +import com.pocket.core.exception.common.ApplicationResponse; +import com.pocket.core.image.dto.PresignedUrlRequest; +import com.pocket.core.image.dto.PresignedUrlResponse; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.media.Content; +import io.swagger.v3.oas.annotations.media.Schema; +import io.swagger.v3.oas.annotations.responses.ApiResponse; +import io.swagger.v3.oas.annotations.responses.ApiResponses; import io.swagger.v3.oas.annotations.tags.Tag; +import org.springframework.web.bind.annotation.RequestBody; @Tag(name = "File", description = "File API") public interface FileControllerDocs { + + @ApiResponses(value = { + @ApiResponse(responseCode = "200", description = "OK"), + @ApiResponse(responseCode = "400", description = "BAD REQUEST", + content = {@Content(schema = @Schema(implementation = ErrorResponse.class))}), + @ApiResponse(responseCode = "500", description = "INTERNAL SERVER ERROR", + content = {@Content(schema = @Schema(implementation = ErrorResponse.class))}) + }) + @Operation(summary = "Presigned URL 요청", description = "파일 업로드를 위한 presigned URL을 생성하는 API") + ApplicationResponse uploadPhoto( + @RequestBody PresignedUrlRequest presignedUrlRequest + ); } diff --git a/inbounds/src/main/java/com/pocket/inbounds/photobooth/presentation/PhotoBoothController.java b/inbounds/src/main/java/com/pocket/inbounds/photobooth/presentation/PhotoBoothController.java index 46d44ed..6dd181c 100644 --- a/inbounds/src/main/java/com/pocket/inbounds/photobooth/presentation/PhotoBoothController.java +++ b/inbounds/src/main/java/com/pocket/inbounds/photobooth/presentation/PhotoBoothController.java @@ -4,18 +4,24 @@ import com.pocket.domain.dto.photobooth.NearPhotoBoothInfo; import com.pocket.domain.dto.photobooth.PhotoBoothFindResponseDto; import com.pocket.domain.entity.photobooth.PhotoBoothBrand; +import com.pocket.domain.port.photobooth.PhotoBoothGetNamePort; import com.pocket.domain.usecase.photobooth.PhotoBoothFindUseCase; +import com.pocket.domain.usecase.photobooth.PhotoBoothGetNameUseCase; +import com.pocket.domain.usecase.photobooth.PhotoBoothGetRatingUseCase; import lombok.RequiredArgsConstructor; import org.springframework.web.bind.annotation.*; +import java.math.BigDecimal; import java.util.List; @RestController @RequiredArgsConstructor -@RequestMapping("/v1/photobooth") +@RequestMapping("/api/v1/photobooth") public class PhotoBoothController implements PhotoBoothControllerDocs { private final PhotoBoothFindUseCase photoBoothFindUseCase; + private final PhotoBoothGetRatingUseCase photoBoothGetRatingUseCase; + private final PhotoBoothGetNameUseCase photoBoothGetNameUseCase; @GetMapping("{id}") public ApplicationResponse getPhotoBoothById(@PathVariable("id") Long id) { @@ -24,6 +30,12 @@ public ApplicationResponse getPhotoBoothById(@PathVar return ApplicationResponse.ok(response); } + @GetMapping("/name/{id}") + public ApplicationResponse getPhotoBoothNameById(@PathVariable("id") Long id) { + String response = photoBoothGetNameUseCase.getPhotoBoothName(id); + return ApplicationResponse.ok(response); + } + @GetMapping() public ApplicationResponse> getAllPhotoBooth( @RequestParam("lat") double lat, @@ -35,4 +47,10 @@ public ApplicationResponse> getAllPhotoBooth( return ApplicationResponse.ok(responses); } + @GetMapping("/rating/{id}") + public ApplicationResponse getPhotoBoothRating(@PathVariable("id") Long id) { + BigDecimal response = photoBoothGetRatingUseCase.getPhotoBoothRating(id); + return ApplicationResponse.ok(response); + } + } diff --git a/inbounds/src/main/java/com/pocket/inbounds/photobooth/presentation/PhotoBoothControllerDocs.java b/inbounds/src/main/java/com/pocket/inbounds/photobooth/presentation/PhotoBoothControllerDocs.java index 250df26..70570c9 100644 --- a/inbounds/src/main/java/com/pocket/inbounds/photobooth/presentation/PhotoBoothControllerDocs.java +++ b/inbounds/src/main/java/com/pocket/inbounds/photobooth/presentation/PhotoBoothControllerDocs.java @@ -1,6 +1,5 @@ package com.pocket.inbounds.photobooth.presentation; - import com.nimbusds.oauth2.sdk.ErrorResponse; import com.pocket.core.exception.common.ApplicationResponse; import com.pocket.domain.dto.photobooth.NearPhotoBoothInfo; @@ -15,6 +14,7 @@ import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.RequestParam; +import java.math.BigDecimal; import java.util.List; @Tag(name = "PhotoBooth", description = "PhotoBooth API") @@ -38,10 +38,32 @@ ApplicationResponse getPhotoBoothById( @ApiResponse(responseCode = "500", description = "INTERNAL SERVER ERROR", content = {@Content(schema = @Schema(implementation = ErrorResponse.class))}) }) - @Operation(summary = "근처 전체 포토부스 정보 제공", description = "id 와 name 만 제공") + @Operation(summary = "포토부스 이름 조회", description = "해당 id 의 포토부스 이름 전달 API") + ApplicationResponse getPhotoBoothNameById( + @PathVariable("id") Long id); + + @ApiResponses(value = { + @ApiResponse(responseCode = "200", description = "OK"), + @ApiResponse(responseCode = "400", description = "BAD REQUEST", + content = {@Content(schema = @Schema(implementation = ErrorResponse.class))}), + @ApiResponse(responseCode = "500", description = "INTERNAL SERVER ERROR", + content = {@Content(schema = @Schema(implementation = ErrorResponse.class))}) + }) + @Operation(summary = "근처 전체 포토부스 정보 제공", description = "사용자의 위치 기반으로 근처 포토부스 목록을 제공하는 API") ApplicationResponse> getAllPhotoBooth( @RequestParam("lat") double lat, @RequestParam("lon") double lon, @RequestParam(value = "brand", required = false) List brand ); + + @ApiResponses(value = { + @ApiResponse(responseCode = "200", description = "OK"), + @ApiResponse(responseCode = "400", description = "BAD REQUEST", + content = {@Content(schema = @Schema(implementation = ErrorResponse.class))}), + @ApiResponse(responseCode = "500", description = "INTERNAL SERVER ERROR", + content = {@Content(schema = @Schema(implementation = ErrorResponse.class))}) + }) + @Operation(summary = "포토부스 평점 조회", description = "해당 id의 포토부스에 대한 평점을 제공하는 API") + ApplicationResponse getPhotoBoothRating( + @PathVariable("id") Long id); } diff --git a/inbounds/src/main/java/com/pocket/inbounds/review/presentation/ReviewController.java b/inbounds/src/main/java/com/pocket/inbounds/review/presentation/ReviewController.java new file mode 100644 index 0000000..2c16428 --- /dev/null +++ b/inbounds/src/main/java/com/pocket/inbounds/review/presentation/ReviewController.java @@ -0,0 +1,74 @@ +package com.pocket.inbounds.review.presentation; + +import com.pocket.core.exception.common.ApplicationResponse; +import com.pocket.domain.dto.review.*; +import com.pocket.domain.dto.user.UserInfoDTO; +import com.pocket.domain.usecase.review.*; +import lombok.RequiredArgsConstructor; +import org.springframework.security.core.annotation.AuthenticationPrincipal; +import org.springframework.security.core.parameters.P; +import org.springframework.web.bind.annotation.*; + +import java.util.List; + +@RestController +@RequiredArgsConstructor +@RequestMapping("/api/v1/review") +public class ReviewController implements ReviewControllerDocs { + + private final ReviewRegisterUseCase reviewRegisterUseCase; + private final ReviewGet6ImagesUseCase reviewGet6ImagesUseCase; + private final ReviewGetRecentUseCase reviewGetRecentUseCase; + private final ReviewGetAllImagesUseCase reviewGetAllImagesUseCase; + private final ReviewBoothFeatureUseCase reviewBoothFeatureUseCase; + private final ReviewPhotoFeatureUseCase reviewPhotoFeatureUseCase; + + @PostMapping + public ApplicationResponse postReview( + @RequestBody ReviewRegisterRequestDto requestDto, + @AuthenticationPrincipal UserInfoDTO user) { + ReviewRegisterResponseDto response = reviewRegisterUseCase.registerReviewResponse(requestDto, user.name()); + return ApplicationResponse.ok(response); + } + + @GetMapping("/images/{photobooth_id}") + public ApplicationResponse getReviewHomeImage( + @PathVariable("photobooth_id") Long photoboothId + ) { + ReviewGet6ImagesResponseDto response = reviewGet6ImagesUseCase.get6Images(photoboothId); + return ApplicationResponse.ok(response); + } + + @GetMapping("/reviews/{photobooth_id}") + public ApplicationResponse getRecentReview( + @PathVariable("photobooth_id") Long photoboothId + ) { + ReviewGetRecentResponseDto response = reviewGetRecentUseCase.getRecentReview(photoboothId); + return ApplicationResponse.ok(response); + } + + @GetMapping("/allimages/{photobooth_id}") + public ApplicationResponse> getReviewImages( + @PathVariable("photobooth_id") Long photoboothId + ) { + List response = reviewGetAllImagesUseCase.getAllImages(photoboothId); + return ApplicationResponse.ok(response); + } + + @GetMapping("/boothfeatures/{photobooth_id}") + public ApplicationResponse> getReviewBoothFeatures( + @PathVariable("photobooth_id") Long photoboothId + ) { + List response = reviewBoothFeatureUseCase.getReviewBoothFeatures(photoboothId); + return ApplicationResponse.ok(response); + } + + @GetMapping("/photofeatures/{photobooth_id}") + public ApplicationResponse> getReviewPhotoFeatures( + @PathVariable("photobooth_id") Long photoboothId + ) { + List response = reviewPhotoFeatureUseCase.getReviewPhotoFeatures(photoboothId); + return ApplicationResponse.ok(response); + } + +} diff --git a/inbounds/src/main/java/com/pocket/inbounds/review/presentation/ReviewControllerDocs.java b/inbounds/src/main/java/com/pocket/inbounds/review/presentation/ReviewControllerDocs.java new file mode 100644 index 0000000..71a8046 --- /dev/null +++ b/inbounds/src/main/java/com/pocket/inbounds/review/presentation/ReviewControllerDocs.java @@ -0,0 +1,94 @@ +package com.pocket.inbounds.review.presentation; + +import com.nimbusds.oauth2.sdk.ErrorResponse; +import com.pocket.core.exception.common.ApplicationResponse; +import com.pocket.domain.dto.review.*; +import com.pocket.domain.dto.user.UserInfoDTO; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.media.Content; +import io.swagger.v3.oas.annotations.media.Schema; +import io.swagger.v3.oas.annotations.responses.ApiResponse; +import io.swagger.v3.oas.annotations.responses.ApiResponses; +import io.swagger.v3.oas.annotations.tags.Tag; +import org.springframework.security.core.annotation.AuthenticationPrincipal; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.RequestBody; + +import java.util.List; + +@Tag(name = "Review", description = "Review API") +public interface ReviewControllerDocs { + + @ApiResponses(value = { + @ApiResponse(responseCode = "200", description = "OK"), + @ApiResponse(responseCode = "400", description = "BAD REQUEST", + content = {@Content(schema = @Schema(implementation = ErrorResponse.class))}), + @ApiResponse(responseCode = "500", description = "INTERNAL SERVER ERROR", + content = {@Content(schema = @Schema(implementation = ErrorResponse.class))}) + }) + @Operation(summary = "리뷰 등록", description = "리뷰를 등록하는 API") + ApplicationResponse postReview( + @RequestBody ReviewRegisterRequestDto requestDto, + @AuthenticationPrincipal UserInfoDTO user + ); + + @ApiResponses(value = { + @ApiResponse(responseCode = "200", description = "OK"), + @ApiResponse(responseCode = "400", description = "BAD REQUEST", + content = {@Content(schema = @Schema(implementation = ErrorResponse.class))}), + @ApiResponse(responseCode = "500", description = "INTERNAL SERVER ERROR", + content = {@Content(schema = @Schema(implementation = ErrorResponse.class))}) + }) + @Operation(summary = "포토부스 6개의 리뷰 이미지 조회", description = "특정 포토부스의 최신 6개 리뷰 이미지를 가져오는 API") + ApplicationResponse getReviewHomeImage( + @PathVariable("photobooth_id") Long photoboothId + ); + + @ApiResponses(value = { + @ApiResponse(responseCode = "200", description = "OK"), + @ApiResponse(responseCode = "400", description = "BAD REQUEST", + content = {@Content(schema = @Schema(implementation = ErrorResponse.class))}), + @ApiResponse(responseCode = "500", description = "INTERNAL SERVER ERROR", + content = {@Content(schema = @Schema(implementation = ErrorResponse.class))}) + }) + @Operation(summary = "최신 리뷰 조회", description = "특정 포토부스에 대한 최신 리뷰를 조회하는 API") + ApplicationResponse getRecentReview( + @PathVariable("photobooth_id") Long photoboothId + ); + + @ApiResponses(value = { + @ApiResponse(responseCode = "200", description = "OK"), + @ApiResponse(responseCode = "400", description = "BAD REQUEST", + content = {@Content(schema = @Schema(implementation = ErrorResponse.class))}), + @ApiResponse(responseCode = "500", description = "INTERNAL SERVER ERROR", + content = {@Content(schema = @Schema(implementation = ErrorResponse.class))}) + }) + @Operation(summary = "포토부스 전체 리뷰 이미지 조회", description = "특정 포토부스에 대한 전체 리뷰 이미지를 조회하는 API") + ApplicationResponse> getReviewImages( + @PathVariable("photobooth_id") Long photoboothId + ); + + @ApiResponses(value = { + @ApiResponse(responseCode = "200", description = "OK"), + @ApiResponse(responseCode = "400", description = "BAD REQUEST", + content = {@Content(schema = @Schema(implementation = ErrorResponse.class))}), + @ApiResponse(responseCode = "500", description = "INTERNAL SERVER ERROR", + content = {@Content(schema = @Schema(implementation = ErrorResponse.class))}) + }) + @Operation(summary = "포토부스 특징 조회", description = "특정 포토부스의 특징을 조회하는 API") + ApplicationResponse> getReviewBoothFeatures( + @PathVariable("photobooth_id") Long photoboothId + ); + + @ApiResponses(value = { + @ApiResponse(responseCode = "200", description = "OK"), + @ApiResponse(responseCode = "400", description = "BAD REQUEST", + content = {@Content(schema = @Schema(implementation = ErrorResponse.class))}), + @ApiResponse(responseCode = "500", description = "INTERNAL SERVER ERROR", + content = {@Content(schema = @Schema(implementation = ErrorResponse.class))}) + }) + @Operation(summary = "포토 특징 조회", description = "특정 포토부스에서 찍힌 사진의 특징을 조회하는 API") + ApplicationResponse> getReviewPhotoFeatures( + @PathVariable("photobooth_id") Long photoboothId + ); +} diff --git a/outbound/build.gradle b/outbound/build.gradle index 15480cf..37236df 100644 --- a/outbound/build.gradle +++ b/outbound/build.gradle @@ -1,5 +1,4 @@ dependencies { - implementation project(':core') implementation project(':domain') @@ -9,15 +8,35 @@ dependencies { implementation 'org.springframework.boot:spring-boot-starter-oauth2-client' implementation 'org.springframework.boot:spring-boot-starter-web' implementation 'org.springframework.boot:spring-boot-starter-data-jpa' + implementation 'jakarta.persistence:jakarta.persistence-api:3.0.0' testImplementation 'io.rest-assured:rest-assured' + // QueryDSL 의존성 추가 + implementation 'com.querydsl:querydsl-jpa:5.0.0' + annotationProcessor 'com.querydsl:querydsl-apt:5.0.0:jpa' + // Jwt implementation 'io.jsonwebtoken:jjwt-api:0.11.5' runtimeOnly 'io.jsonwebtoken:jjwt-impl:0.11.5' runtimeOnly 'io.jsonwebtoken:jjwt-jackson:0.11.5' } +def querydslDir = "$buildDir/generated/querydsl" + +querydsl { + jpa = true + querydslSourcesDir = querydslDir +} + +sourceSets { + main.java.srcDir querydslDir +} + +compileJava { + options.annotationProcessorGeneratedSourcesDirectory = file(querydslDir) +} + bootJar { enabled = false } @@ -26,4 +45,4 @@ jar { enabled = true } -tasks.register("prepareKotlinBuildScriptModel"){} +tasks.register("prepareKotlinBuildScriptModel") {} diff --git a/outbound/src/main/java/com/pocket/outbound/adapter/album/AlbumAdapter.java b/outbound/src/main/java/com/pocket/outbound/adapter/album/adapter/AlbumRegisterAdapter.java similarity index 70% rename from outbound/src/main/java/com/pocket/outbound/adapter/album/AlbumAdapter.java rename to outbound/src/main/java/com/pocket/outbound/adapter/album/adapter/AlbumRegisterAdapter.java index 2d2b09c..bd7afd2 100644 --- a/outbound/src/main/java/com/pocket/outbound/adapter/album/AlbumAdapter.java +++ b/outbound/src/main/java/com/pocket/outbound/adapter/album/adapter/AlbumRegisterAdapter.java @@ -1,32 +1,28 @@ -package com.pocket.outbound.adapter.album; +package com.pocket.outbound.adapter.album.adapter; import com.pocket.core.aop.annotation.AdapterService; import com.pocket.core.exception.photobooth.PhotoBoothCustomException; import com.pocket.core.exception.photobooth.PhotoBoothErrorCode; import com.pocket.core.exception.user.UserCustomException; import com.pocket.core.exception.user.UserErrorCode; -import com.pocket.core.image.service.FileService; import com.pocket.domain.dto.album.AlbumRegisterRequestDto; import com.pocket.domain.dto.album.AlbumRegisterResponseDto; -import com.pocket.domain.entity.album.HashTag; -import com.pocket.domain.entity.album.Memo; -import com.pocket.domain.entity.image.Image; -import com.pocket.domain.entity.image.ImageType; import com.pocket.domain.port.album.AlbumRegisterPort; +import com.pocket.outbound.adapter.album.mapper.AlbumOutBoundMapper; import com.pocket.outbound.entity.*; import com.pocket.outbound.repository.*; import lombok.RequiredArgsConstructor; @AdapterService @RequiredArgsConstructor -public class AlbumAdapter implements AlbumRegisterPort { +public class AlbumRegisterAdapter implements AlbumRegisterPort { - private final PhotoRepository photoRepository; + private final AlbumRepository albumRepository; private final HashTagRepository hashtagRepository; private final AlbumHashTagRepository photoHashtagRepository; private final PhotoBoothRepository photoBoothRepository; private final UserRepository userRepository; - private final AlbumMapper albumMapper; + private final AlbumOutBoundMapper albumOutBoundMapper; @Override public AlbumRegisterResponseDto registerPhoto(AlbumRegisterRequestDto dto, String name) { @@ -37,12 +33,12 @@ public AlbumRegisterResponseDto registerPhoto(AlbumRegisterRequestDto dto, Strin JpaUser jpaUser = userRepository.findByUserName(name) .orElseThrow(() -> new UserCustomException(UserErrorCode.NO_USER_INFO)); - JpaAlbum photoEntity = albumMapper.toJpaAlbum(dto, photoBooth, jpaUser); - photoRepository.save(photoEntity); + JpaAlbum photoEntity = albumOutBoundMapper.toJpaAlbum(dto, photoBooth, jpaUser); + albumRepository.save(photoEntity); for (String hashtag : dto.hashtag()) { - JpaHashTag hashtagEntity = albumMapper.toJpaHashTag(hashtag, jpaUser); - JpaAlbumHashTag imageHashtagEntity = albumMapper.toJpaAlbumHashTag(photoEntity, hashtagEntity); + JpaHashTag hashtagEntity = albumOutBoundMapper.toJpaHashTag(hashtag, jpaUser); + JpaAlbumHashTag imageHashtagEntity = albumOutBoundMapper.toJpaAlbumHashTag(photoEntity, hashtagEntity); hashtagRepository.save(hashtagEntity); photoHashtagRepository.save(imageHashtagEntity); diff --git a/outbound/src/main/java/com/pocket/outbound/adapter/album/AlbumMapper.java b/outbound/src/main/java/com/pocket/outbound/adapter/album/mapper/AlbumOutBoundMapper.java similarity index 91% rename from outbound/src/main/java/com/pocket/outbound/adapter/album/AlbumMapper.java rename to outbound/src/main/java/com/pocket/outbound/adapter/album/mapper/AlbumOutBoundMapper.java index ca6a13b..56f914f 100644 --- a/outbound/src/main/java/com/pocket/outbound/adapter/album/AlbumMapper.java +++ b/outbound/src/main/java/com/pocket/outbound/adapter/album/mapper/AlbumOutBoundMapper.java @@ -1,4 +1,4 @@ -package com.pocket.outbound.adapter.album; +package com.pocket.outbound.adapter.album.mapper; import com.pocket.domain.dto.album.AlbumRegisterRequestDto; import com.pocket.domain.entity.album.HashTag; @@ -15,12 +15,12 @@ @Component @RequiredArgsConstructor -public class AlbumMapper { +public class AlbumOutBoundMapper { public JpaAlbum toJpaAlbum(AlbumRegisterRequestDto dto, JpaPhotoBooth photoBooth, JpaUser jpaUser) { Memo memo = new Memo(dto.memo()); Image image = new Image(ImageType.PHOTO); - image.makeImage(dto, dto.filePath()); + image.makeAlbumImage(dto, dto.filePath()); return JpaAlbum.builder() .photoBooth(photoBooth) diff --git a/outbound/src/main/java/com/pocket/outbound/adapter/photobooth/adpater/PhotoBoothFindAdapter.java b/outbound/src/main/java/com/pocket/outbound/adapter/photobooth/adapter/PhotoBoothFindAdapter.java similarity index 98% rename from outbound/src/main/java/com/pocket/outbound/adapter/photobooth/adpater/PhotoBoothFindAdapter.java rename to outbound/src/main/java/com/pocket/outbound/adapter/photobooth/adapter/PhotoBoothFindAdapter.java index 4d4a369..31328c8 100644 --- a/outbound/src/main/java/com/pocket/outbound/adapter/photobooth/adpater/PhotoBoothFindAdapter.java +++ b/outbound/src/main/java/com/pocket/outbound/adapter/photobooth/adapter/PhotoBoothFindAdapter.java @@ -1,4 +1,4 @@ -package com.pocket.outbound.adapter.photobooth.adpater; +package com.pocket.outbound.adapter.photobooth.adapter; import com.pocket.core.aop.annotation.AdapterService; import com.pocket.core.exception.photobooth.PhotoBoothCustomException; diff --git a/outbound/src/main/java/com/pocket/outbound/adapter/photobooth/adapter/PhotoBoothGetNameAdapter.java b/outbound/src/main/java/com/pocket/outbound/adapter/photobooth/adapter/PhotoBoothGetNameAdapter.java new file mode 100644 index 0000000..25894b3 --- /dev/null +++ b/outbound/src/main/java/com/pocket/outbound/adapter/photobooth/adapter/PhotoBoothGetNameAdapter.java @@ -0,0 +1,26 @@ +package com.pocket.outbound.adapter.photobooth.adapter; + +import com.pocket.core.aop.annotation.AdapterService; +import com.pocket.core.exception.photobooth.PhotoBoothCustomException; +import com.pocket.core.exception.photobooth.PhotoBoothErrorCode; +import com.pocket.domain.port.photobooth.PhotoBoothGetNamePort; +import com.pocket.outbound.entity.JpaPhotoBooth; +import com.pocket.outbound.repository.PhotoBoothRepository; +import lombok.RequiredArgsConstructor; + + +@AdapterService +@RequiredArgsConstructor +public class PhotoBoothGetNameAdapter implements PhotoBoothGetNamePort { + + private final PhotoBoothRepository photoBoothRepository; + + @Override + public String getPhotoBoothName(Long photoboothId) { + + JpaPhotoBooth photoBooth = photoBoothRepository.findById(photoboothId) + .orElseThrow(() -> new PhotoBoothCustomException(PhotoBoothErrorCode.PHOTOBOOTH_NOT_FOUND)); + + return photoBooth.getPhotoBooth().getName(); + } +} diff --git a/outbound/src/main/java/com/pocket/outbound/adapter/photobooth/adapter/PhotoBoothGetRatingAdapter.java b/outbound/src/main/java/com/pocket/outbound/adapter/photobooth/adapter/PhotoBoothGetRatingAdapter.java new file mode 100644 index 0000000..c0f9d5e --- /dev/null +++ b/outbound/src/main/java/com/pocket/outbound/adapter/photobooth/adapter/PhotoBoothGetRatingAdapter.java @@ -0,0 +1,26 @@ +package com.pocket.outbound.adapter.photobooth.adapter; + +import com.pocket.core.aop.annotation.AdapterService; +import com.pocket.core.exception.photobooth.PhotoBoothCustomException; +import com.pocket.core.exception.photobooth.PhotoBoothErrorCode; +import com.pocket.domain.port.photobooth.PhotoBoothGetRatingPort; +import com.pocket.outbound.entity.JpaPhotoBooth; +import com.pocket.outbound.repository.PhotoBoothRepository; +import lombok.RequiredArgsConstructor; + +import java.math.BigDecimal; + +@AdapterService +@RequiredArgsConstructor +public class PhotoBoothGetRatingAdapter implements PhotoBoothGetRatingPort { + + private final PhotoBoothRepository photoBoothRepository; + + @Override + public BigDecimal getRating(Long photoboothId) { + JpaPhotoBooth photoBooth = photoBoothRepository.findById(photoboothId) + .orElseThrow(() -> new PhotoBoothCustomException(PhotoBoothErrorCode.PHOTOBOOTH_NOT_FOUND)); + + return photoBooth.getRating(); + } +} diff --git a/outbound/src/main/java/com/pocket/outbound/adapter/review/adapter/ReviewBoothFeatureAdapter.java b/outbound/src/main/java/com/pocket/outbound/adapter/review/adapter/ReviewBoothFeatureAdapter.java new file mode 100644 index 0000000..6692aab --- /dev/null +++ b/outbound/src/main/java/com/pocket/outbound/adapter/review/adapter/ReviewBoothFeatureAdapter.java @@ -0,0 +1,35 @@ +package com.pocket.outbound.adapter.review.adapter; + + +import com.pocket.core.aop.annotation.AdapterService; +import com.pocket.domain.dto.review.ReviewBoothFeatureDto; +import com.pocket.domain.entity.review.BoothFeature; +import com.pocket.domain.port.review.ReviewBoothFeaturePort; +import com.pocket.outbound.adapter.review.dto.BoothFeatureDTO; +import com.pocket.outbound.repository.review.ReviewRepository; +import lombok.RequiredArgsConstructor; + +import java.util.List; +import java.util.stream.Collectors; + +@AdapterService +@RequiredArgsConstructor +public class ReviewBoothFeatureAdapter implements ReviewBoothFeaturePort { + + private final ReviewRepository reviewRepository; + + @Override + public List getReviewBoothFeature(Long photoboothId) { + // Repository에서 상위 4개의 BoothFeature를 조회 + List result = reviewRepository.findTop4ByBoothFeature(photoboothId); + + // BoothFeatureDTO에서 직접 값을 가져와서 ReviewBoothFeatureDto로 변환 + return result.stream() + .map(dto -> new ReviewBoothFeatureDto( + dto.boothFeature().stream().map(BoothFeature::getDescription).toList(), + (int) dto.featureCount())) + .limit(4) // 상위 4개의 결과만 반환 + .collect(Collectors.toList()); + } + +} diff --git a/outbound/src/main/java/com/pocket/outbound/adapter/review/adapter/ReviewGet6ImagesAdapter.java b/outbound/src/main/java/com/pocket/outbound/adapter/review/adapter/ReviewGet6ImagesAdapter.java new file mode 100644 index 0000000..e2db57d --- /dev/null +++ b/outbound/src/main/java/com/pocket/outbound/adapter/review/adapter/ReviewGet6ImagesAdapter.java @@ -0,0 +1,35 @@ +package com.pocket.outbound.adapter.review.adapter; + + +import com.pocket.core.aop.annotation.AdapterService; +import com.pocket.domain.dto.review.ReviewGet6ImagesResponseDto; +import com.pocket.domain.port.review.ReviewGet6ImagesPort; +import com.pocket.outbound.entity.JpaReviewImage; +import com.pocket.outbound.repository.ReviewImageRepository; +import lombok.RequiredArgsConstructor; + +import java.util.List; +import java.util.stream.Collectors; + +@AdapterService +@RequiredArgsConstructor +public class ReviewGet6ImagesAdapter implements ReviewGet6ImagesPort { + + private final ReviewImageRepository reviewImageRepository; + + @Override + public ReviewGet6ImagesResponseDto get6Images(Long photoboothId) { + List reviewImages = reviewImageRepository.findTop6ByReviewPhotoBoothIdOrderByReviewIdDesc(photoboothId); + + // 포토부스에 해당하는 전체 리뷰 이미지 개수 구하기 + int totalImageCount = reviewImageRepository.countByReviewPhotoBoothId(photoboothId); + + List imageUrls = reviewImages.stream() + .map(reviewImage -> reviewImage.getImage().getImageUrl()) + .collect(Collectors.toList()); + + return new ReviewGet6ImagesResponseDto(imageUrls, totalImageCount); + } + + +} diff --git a/outbound/src/main/java/com/pocket/outbound/adapter/review/adapter/ReviewGetAllImagesAdapter.java b/outbound/src/main/java/com/pocket/outbound/adapter/review/adapter/ReviewGetAllImagesAdapter.java new file mode 100644 index 0000000..c54e9e8 --- /dev/null +++ b/outbound/src/main/java/com/pocket/outbound/adapter/review/adapter/ReviewGetAllImagesAdapter.java @@ -0,0 +1,28 @@ +package com.pocket.outbound.adapter.review.adapter; + + +import com.pocket.core.aop.annotation.AdapterService; +import com.pocket.domain.port.review.ReviewGetAllImagesPort; +import com.pocket.outbound.entity.JpaReviewImage; +import com.pocket.outbound.repository.ReviewImageRepository; +import lombok.RequiredArgsConstructor; + +import java.util.List; +import java.util.stream.Collectors; + +@AdapterService +@RequiredArgsConstructor +public class ReviewGetAllImagesAdapter implements ReviewGetAllImagesPort { + + private final ReviewImageRepository reviewImageRepository; + + @Override + public List getAllImages(Long photoboothId) { + + List reviewImages = reviewImageRepository.findByReviewPhotoBoothId(photoboothId); + + return reviewImages.stream() + .map(reviewImage -> reviewImage.getImage().getImageUrl()) + .collect(Collectors.toList()); + } +} diff --git a/outbound/src/main/java/com/pocket/outbound/adapter/review/adapter/ReviewGetRecentAdapter.java b/outbound/src/main/java/com/pocket/outbound/adapter/review/adapter/ReviewGetRecentAdapter.java new file mode 100644 index 0000000..3d5a666 --- /dev/null +++ b/outbound/src/main/java/com/pocket/outbound/adapter/review/adapter/ReviewGetRecentAdapter.java @@ -0,0 +1,42 @@ +package com.pocket.outbound.adapter.review.adapter; + + +import com.pocket.core.aop.annotation.AdapterService; +import com.pocket.domain.dto.review.ReviewGetRecentResponseDto; +import com.pocket.domain.dto.review.ReviewPreviewDto; +import com.pocket.domain.port.review.ReviewGetRecentPort; +import com.pocket.outbound.adapter.review.mapper.ReviewOutBoundMapper; +import com.pocket.outbound.entity.JpaReview; +import com.pocket.outbound.entity.JpaReviewImage; +import com.pocket.outbound.repository.ReviewImageRepository; +import com.pocket.outbound.repository.review.ReviewRepository; +import lombok.RequiredArgsConstructor; + +import java.util.List; +import java.util.stream.Collectors; + +@AdapterService +@RequiredArgsConstructor +public class ReviewGetRecentAdapter implements ReviewGetRecentPort { + + private final ReviewRepository reviewRepository; + private final ReviewImageRepository reviewImageRepository; + private final ReviewOutBoundMapper reviewOutBoundMapper; + + @Override + public ReviewGetRecentResponseDto getRecentReview(Long photoboothId) { + int totalReviewCount = reviewRepository.countByPhotoBoothId(photoboothId); + + List recentReviews = reviewRepository.findTop2ByPhotoBoothIdOrderByIdDesc(photoboothId); + + List reviewPreviews = recentReviews.stream().map(review -> { + List images = reviewImageRepository.findByReviewId(review.getId()); + String imageUrl = images.isEmpty() ? "" : images.get(0).getImage().getImageUrl(); + int imageCount = images.size(); + + return reviewOutBoundMapper.toReviewPreviewDto(review, imageUrl, imageCount); + }).collect(Collectors.toList()); + + return new ReviewGetRecentResponseDto(totalReviewCount, reviewPreviews); + } +} diff --git a/outbound/src/main/java/com/pocket/outbound/adapter/review/adapter/ReviewPhotoFeatureAdapter.java b/outbound/src/main/java/com/pocket/outbound/adapter/review/adapter/ReviewPhotoFeatureAdapter.java new file mode 100644 index 0000000..d99d912 --- /dev/null +++ b/outbound/src/main/java/com/pocket/outbound/adapter/review/adapter/ReviewPhotoFeatureAdapter.java @@ -0,0 +1,35 @@ +package com.pocket.outbound.adapter.review.adapter; + + +import com.pocket.core.aop.annotation.AdapterService; +import com.pocket.domain.dto.review.ReviewPhotoFeatureDto; +import com.pocket.domain.entity.review.PhotoFeature; +import com.pocket.domain.port.review.ReviewPhotoFeaturePort; +import com.pocket.outbound.adapter.review.dto.PhotoFeatureDTO; +import com.pocket.outbound.repository.review.ReviewRepository; +import lombok.RequiredArgsConstructor; + +import java.util.List; +import java.util.stream.Collectors; + +@AdapterService +@RequiredArgsConstructor +public class ReviewPhotoFeatureAdapter implements ReviewPhotoFeaturePort { + private final ReviewRepository reviewRepository; + + @Override + public List getReviewPhotoFeature(Long photoboothId) { + + // Repository에서 상위 4개의 PhotoFeature를 조회 + List result = reviewRepository.findTop4ByPhotoFeature(photoboothId); + + // PhotoFeatureDTO에서 직접 값을 가져와서 ReviewPhotoFeatureDto로 변환 + return result.stream() + .map(dto -> new ReviewPhotoFeatureDto( + dto.photoFeature().stream().map(PhotoFeature::getDescription).toList(), + (int) dto.featureCount())) + .limit(4) // 상위 4개의 결과만 반환 + .collect(Collectors.toList()); + } + +} diff --git a/outbound/src/main/java/com/pocket/outbound/adapter/review/adapter/ReviewRegisterAdapter.java b/outbound/src/main/java/com/pocket/outbound/adapter/review/adapter/ReviewRegisterAdapter.java new file mode 100644 index 0000000..6a7d844 --- /dev/null +++ b/outbound/src/main/java/com/pocket/outbound/adapter/review/adapter/ReviewRegisterAdapter.java @@ -0,0 +1,72 @@ +package com.pocket.outbound.adapter.review.adapter; + +import com.pocket.core.aop.annotation.AdapterService; +import com.pocket.core.exception.photobooth.PhotoBoothCustomException; +import com.pocket.core.exception.photobooth.PhotoBoothErrorCode; +import com.pocket.core.exception.user.UserCustomException; +import com.pocket.core.exception.user.UserErrorCode; +import com.pocket.domain.dto.review.ReviewRegisterRequestDto; +import com.pocket.domain.dto.review.ReviewRegisterResponseDto; +import com.pocket.domain.entity.image.Image; +import com.pocket.domain.entity.image.ImageType; +import com.pocket.domain.port.review.ReviewRegisterPort; +import com.pocket.outbound.adapter.review.mapper.ReviewOutBoundMapper; +import com.pocket.outbound.entity.JpaPhotoBooth; +import com.pocket.outbound.entity.JpaReview; +import com.pocket.outbound.entity.JpaReviewImage; +import com.pocket.outbound.entity.JpaUser; +import com.pocket.outbound.repository.*; +import com.pocket.outbound.repository.review.ReviewRepository; +import lombok.RequiredArgsConstructor; + + +@AdapterService +@RequiredArgsConstructor +public class ReviewRegisterAdapter implements ReviewRegisterPort { + + private final PhotoBoothRepository photoBoothRepository; + private final UserRepository userRepository; + private final ReviewRepository reviewRepository; + private final ReviewOutBoundMapper reviewOutBoundMapper; + private final ReviewImageRepository reviewImageRepository; + + + @Override + public ReviewRegisterResponseDto registerReview(ReviewRegisterRequestDto dto, String name) { + + JpaPhotoBooth photoBooth = photoBoothRepository.findById(dto.photoboothId()) + .orElseThrow(() -> new PhotoBoothCustomException(PhotoBoothErrorCode.PHOTOBOOTH_NOT_FOUND)); + + JpaUser jpaUser = userRepository.findByUserName(name) + .orElseThrow(() -> new UserCustomException(UserErrorCode.NO_USER_INFO)); + + JpaReview reviewEntity = reviewOutBoundMapper.toJpaReview(dto, photoBooth, jpaUser); + reviewRepository.save(reviewEntity); + + + + for (String filePath : dto.filePaths()) { + Image image = new Image(ImageType.REVIEW); + image.makeReviewImage(filePath); + JpaReviewImage reviewImage = JpaReviewImage.builder() + .image(image) + .review(reviewEntity) + .build(); + + reviewImageRepository.save(reviewImage); + } + + // 포토부스의 별점 업데이트 + photoBooth.updateRating(dto.rating()); + photoBoothRepository.save(photoBooth); + + return new ReviewRegisterResponseDto( + dto.photoboothId(), + dto.rating(), + dto.boothFeatures(), + dto.photoFeatures(), + dto.filePaths(), + dto.content() + ); + } +} diff --git a/outbound/src/main/java/com/pocket/outbound/adapter/review/dto/BoothFeatureDTO.java b/outbound/src/main/java/com/pocket/outbound/adapter/review/dto/BoothFeatureDTO.java new file mode 100644 index 0000000..32d3b31 --- /dev/null +++ b/outbound/src/main/java/com/pocket/outbound/adapter/review/dto/BoothFeatureDTO.java @@ -0,0 +1,11 @@ +package com.pocket.outbound.adapter.review.dto; + +import com.pocket.domain.entity.review.BoothFeature; + +import java.util.List; + +public record BoothFeatureDTO( + List boothFeature, + long featureCount) { +} + diff --git a/outbound/src/main/java/com/pocket/outbound/adapter/review/dto/PhotoFeatureDTO.java b/outbound/src/main/java/com/pocket/outbound/adapter/review/dto/PhotoFeatureDTO.java new file mode 100644 index 0000000..bd295ca --- /dev/null +++ b/outbound/src/main/java/com/pocket/outbound/adapter/review/dto/PhotoFeatureDTO.java @@ -0,0 +1,10 @@ +package com.pocket.outbound.adapter.review.dto; + +import com.pocket.domain.entity.review.PhotoFeature; + +import java.util.List; + +public record PhotoFeatureDTO( + List photoFeature, + long featureCount) { +} diff --git a/outbound/src/main/java/com/pocket/outbound/adapter/review/mapper/ReviewOutBoundMapper.java b/outbound/src/main/java/com/pocket/outbound/adapter/review/mapper/ReviewOutBoundMapper.java new file mode 100644 index 0000000..2b8f843 --- /dev/null +++ b/outbound/src/main/java/com/pocket/outbound/adapter/review/mapper/ReviewOutBoundMapper.java @@ -0,0 +1,83 @@ +package com.pocket.outbound.adapter.review.mapper; + +import com.pocket.domain.dto.review.ReviewPreviewDto; +import com.pocket.domain.dto.review.ReviewRegisterRequestDto; +import com.pocket.domain.entity.review.BoothFeature; +import com.pocket.domain.entity.review.PhotoFeature; +import com.pocket.domain.entity.review.Review; +import com.pocket.outbound.entity.JpaPhotoBooth; +import com.pocket.outbound.entity.JpaReview; +import com.pocket.outbound.entity.JpaUser; +import org.springframework.stereotype.Component; + +import java.time.LocalDateTime; +import java.util.List; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +@Component +public class ReviewOutBoundMapper { + + public JpaReview toJpaReview(ReviewRegisterRequestDto dto, JpaPhotoBooth photoBooth, JpaUser jpaUser) { + + // 한글로 받은 photoFeatures를 PhotoFeature enum으로 변환 + List photoFeatures = dto.photoFeatures().stream() + .map(PhotoFeature::fromDescription) // 한글 설명을 enum으로 변환 + .collect(Collectors.toList()); + + // 한글로 받은 boothFeatures를 BoothFeature enum으로 변환 + List boothFeatures = dto.boothFeatures().stream() + .map(BoothFeature::fromDescription) // 한글 설명을 enum으로 변환 + .collect(Collectors.toList()); + + // Review 엔티티 생성 + Review review = new Review( + dto.rating(), + dto.content(), + boothFeatures, + photoFeatures + ); + + return JpaReview.builder() + .photoBooth(photoBooth) + .jpaUser(jpaUser) + .review(review) + .build(); + } + + // 문자열을 PhotoFeature enum으로 변환 + private PhotoFeature convertToPhotoFeature(String feature) { + return PhotoFeature.valueOf(feature.toUpperCase().replace(" ", "_")); + } + + // 문자열을 BoothFeature enum으로 변환 + private BoothFeature convertToBoothFeature(String feature) { + return BoothFeature.valueOf(feature.toUpperCase().replace(" ", "_")); + } + + public ReviewPreviewDto toReviewPreviewDto(JpaReview review, String imageUrl, int imageCount) { + String name = review.getJpaUser().getUser().getName(); + LocalDateTime createdAt = review.getReview().getCreatedAt(); + String year = String.valueOf(createdAt.getYear()); + String month = String.format("%02d", createdAt.getMonthValue()); + String date = String.format("%02d", createdAt.getDayOfMonth()); + + List features = Stream.concat( + review.getReview().getBoothFeatures().stream().map(BoothFeature::getDescription), + review.getReview().getPhotoFeatures().stream().map(PhotoFeature::getDescription) + ).collect(Collectors.toList()); + + return new ReviewPreviewDto( + review.getId(), + name, + year, + month, + date, + review.getReview().getContent(), + features, + imageUrl, + imageCount + ); + } + +} diff --git a/outbound/src/main/java/com/pocket/outbound/entity/JpaPhotoBooth.java b/outbound/src/main/java/com/pocket/outbound/entity/JpaPhotoBooth.java index ce931e2..00fc26a 100644 --- a/outbound/src/main/java/com/pocket/outbound/entity/JpaPhotoBooth.java +++ b/outbound/src/main/java/com/pocket/outbound/entity/JpaPhotoBooth.java @@ -5,6 +5,8 @@ import lombok.Getter; import lombok.NoArgsConstructor; +import java.math.BigDecimal; + @Getter @NoArgsConstructor @Entity @@ -23,4 +25,12 @@ public void setPhotoBooth(PhotoBooth photoBooth) { this.photoBooth = photoBooth; } + public void updateRating(int newRating) { + photoBooth.updateRating(newRating); + } + + public BigDecimal getRating() { + return photoBooth.getAverageRating(); + } + } diff --git a/outbound/src/main/java/com/pocket/outbound/entity/JpaReview.java b/outbound/src/main/java/com/pocket/outbound/entity/JpaReview.java index 646ee4b..44e673f 100644 --- a/outbound/src/main/java/com/pocket/outbound/entity/JpaReview.java +++ b/outbound/src/main/java/com/pocket/outbound/entity/JpaReview.java @@ -2,6 +2,8 @@ import com.pocket.domain.entity.review.Review; import jakarta.persistence.*; +import lombok.AllArgsConstructor; +import lombok.Builder; import lombok.Getter; import lombok.NoArgsConstructor; @@ -9,6 +11,8 @@ @NoArgsConstructor @Entity @Table(name = "REVIEW") +@Builder +@AllArgsConstructor public class JpaReview { @Id @@ -17,7 +21,7 @@ public class JpaReview { private Long id; @ManyToOne(fetch = FetchType.LAZY) - private JpaUser user; + private JpaUser jpaUser; @ManyToOne(fetch = FetchType.LAZY) private JpaPhotoBooth photoBooth; diff --git a/outbound/src/main/java/com/pocket/outbound/entity/JpaReviewImage.java b/outbound/src/main/java/com/pocket/outbound/entity/JpaReviewImage.java index bfd89ac..f119b0e 100644 --- a/outbound/src/main/java/com/pocket/outbound/entity/JpaReviewImage.java +++ b/outbound/src/main/java/com/pocket/outbound/entity/JpaReviewImage.java @@ -2,6 +2,8 @@ import com.pocket.domain.entity.image.Image; import jakarta.persistence.*; +import lombok.AllArgsConstructor; +import lombok.Builder; import lombok.Getter; import lombok.NoArgsConstructor; @@ -11,6 +13,8 @@ @NoArgsConstructor @Entity @Table(name = "REVIEWIMAGE") +@Builder +@AllArgsConstructor public class JpaReviewImage { @Id diff --git a/outbound/src/main/java/com/pocket/outbound/repository/PhotoRepository.java b/outbound/src/main/java/com/pocket/outbound/repository/AlbumRepository.java similarity index 71% rename from outbound/src/main/java/com/pocket/outbound/repository/PhotoRepository.java rename to outbound/src/main/java/com/pocket/outbound/repository/AlbumRepository.java index f354c66..aa08b8e 100644 --- a/outbound/src/main/java/com/pocket/outbound/repository/PhotoRepository.java +++ b/outbound/src/main/java/com/pocket/outbound/repository/AlbumRepository.java @@ -3,6 +3,6 @@ import com.pocket.outbound.entity.JpaAlbum; import org.springframework.data.jpa.repository.JpaRepository; -public interface PhotoRepository extends JpaRepository { +public interface AlbumRepository extends JpaRepository { } diff --git a/outbound/src/main/java/com/pocket/outbound/repository/ReviewImageRepository.java b/outbound/src/main/java/com/pocket/outbound/repository/ReviewImageRepository.java index 5af79f7..de0b7a4 100644 --- a/outbound/src/main/java/com/pocket/outbound/repository/ReviewImageRepository.java +++ b/outbound/src/main/java/com/pocket/outbound/repository/ReviewImageRepository.java @@ -2,7 +2,19 @@ import com.pocket.outbound.entity.JpaReviewImage; import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.jpa.repository.Query; +import org.springframework.data.repository.query.Param; + +import java.util.List; public interface ReviewImageRepository extends JpaRepository { + List findTop6ByReviewPhotoBoothIdOrderByReviewIdDesc(Long photoboothId); + + int countByReviewPhotoBoothId(Long photoboothId); + + List findByReviewId(Long reviewId); + + List findByReviewPhotoBoothId(Long photoboothId); + } diff --git a/outbound/src/main/java/com/pocket/outbound/repository/ReviewRepository.java b/outbound/src/main/java/com/pocket/outbound/repository/ReviewRepository.java deleted file mode 100644 index 2f66bc5..0000000 --- a/outbound/src/main/java/com/pocket/outbound/repository/ReviewRepository.java +++ /dev/null @@ -1,8 +0,0 @@ -package com.pocket.outbound.repository; - -import com.pocket.outbound.entity.JpaReview; -import org.springframework.data.jpa.repository.JpaRepository; - -public interface ReviewRepository extends JpaRepository { - -} diff --git a/outbound/src/main/java/com/pocket/outbound/repository/review/ReviewRepository.java b/outbound/src/main/java/com/pocket/outbound/repository/review/ReviewRepository.java new file mode 100644 index 0000000..78c2e7f --- /dev/null +++ b/outbound/src/main/java/com/pocket/outbound/repository/review/ReviewRepository.java @@ -0,0 +1,14 @@ +package com.pocket.outbound.repository.review; + +import com.pocket.outbound.entity.JpaReview; +import org.springframework.data.jpa.repository.JpaRepository; + +import java.util.List; + +public interface ReviewRepository extends JpaRepository, ReviewRepositoryCustom { + + int countByPhotoBoothId(Long photoboothId); + + List findTop2ByPhotoBoothIdOrderByIdDesc(Long photoboothId); + +} diff --git a/outbound/src/main/java/com/pocket/outbound/repository/review/ReviewRepositoryCustom.java b/outbound/src/main/java/com/pocket/outbound/repository/review/ReviewRepositoryCustom.java new file mode 100644 index 0000000..3c936c0 --- /dev/null +++ b/outbound/src/main/java/com/pocket/outbound/repository/review/ReviewRepositoryCustom.java @@ -0,0 +1,14 @@ +package com.pocket.outbound.repository.review; + +import com.pocket.outbound.adapter.review.dto.BoothFeatureDTO; +import com.pocket.outbound.adapter.review.dto.PhotoFeatureDTO; + +import java.util.List; + +public interface ReviewRepositoryCustom { + + List findTop4ByBoothFeature(Long photoboothId); + + List findTop4ByPhotoFeature(Long photoboothId); + +} diff --git a/outbound/src/main/java/com/pocket/outbound/repository/review/ReviewRepositoryImpl.java b/outbound/src/main/java/com/pocket/outbound/repository/review/ReviewRepositoryImpl.java new file mode 100644 index 0000000..7a968b9 --- /dev/null +++ b/outbound/src/main/java/com/pocket/outbound/repository/review/ReviewRepositoryImpl.java @@ -0,0 +1,48 @@ +package com.pocket.outbound.repository.review; + +import com.pocket.outbound.adapter.review.dto.BoothFeatureDTO; +import com.pocket.outbound.adapter.review.dto.PhotoFeatureDTO; +import com.pocket.outbound.entity.QJpaReview; +import com.querydsl.core.types.Projections; +import com.querydsl.jpa.impl.JPAQueryFactory; +import lombok.RequiredArgsConstructor; + +import java.util.List; + +@RequiredArgsConstructor +public class ReviewRepositoryImpl implements ReviewRepositoryCustom { + + private final JPAQueryFactory queryFactory; + + @Override + public List findTop4ByBoothFeature(Long photoboothId) { + QJpaReview review = QJpaReview.jpaReview; + + return queryFactory + .select(Projections.constructor(BoothFeatureDTO.class, + review.review.boothFeatures, + review.count())) + .from(review) + .where(review.photoBooth.id.eq(photoboothId)) + .groupBy(review.review.boothFeatures) + .orderBy(review.count().desc()) + .limit(4) + .fetch(); + } + + @Override + public List findTop4ByPhotoFeature(Long photoboothId) { + QJpaReview review = QJpaReview.jpaReview; + + return queryFactory + .select(Projections.constructor(PhotoFeatureDTO.class, + review.review.photoFeatures, // pf (사진 기능) + review.count())) // COUNT(r) as featureCount + .from(review) + .where(review.photoBooth.id.eq(photoboothId)) + .groupBy(review.review.photoFeatures) + .orderBy(review.count().desc()) + .limit(4) + .fetch(); + } +} diff --git a/outbound/src/test/java/com/pocket/outbound/adapter/photobooth/adpater/PhotoBoothFindAdapterTest.java b/outbound/src/test/java/com/pocket/outbound/adapter/photobooth/adapter/PhotoBoothFindAdapterTest.java similarity index 99% rename from outbound/src/test/java/com/pocket/outbound/adapter/photobooth/adpater/PhotoBoothFindAdapterTest.java rename to outbound/src/test/java/com/pocket/outbound/adapter/photobooth/adapter/PhotoBoothFindAdapterTest.java index 4cf3f68..ea34de8 100644 --- a/outbound/src/test/java/com/pocket/outbound/adapter/photobooth/adpater/PhotoBoothFindAdapterTest.java +++ b/outbound/src/test/java/com/pocket/outbound/adapter/photobooth/adapter/PhotoBoothFindAdapterTest.java @@ -1,4 +1,4 @@ -package com.pocket.outbound.adapter.photobooth.adpater; +package com.pocket.outbound.adapter.photobooth.adapter; import com.pocket.domain.dto.photobooth.NearPhotoBoothInfo; import com.pocket.domain.entity.photobooth.PhotoBooth;