From c3b562661db069a57f2a4b1fe46782d5e5ea04ad Mon Sep 17 00:00:00 2001 From: inswal843 Date: Tue, 28 Jan 2025 14:44:46 +0900 Subject: [PATCH 1/6] =?UTF-8?q?[Feat]=20=EC=B5=9C=EA=B7=BC=20=EB=B3=B8=20?= =?UTF-8?q?=EC=83=81=ED=92=88=20=EA=B8=B0=EB=8A=A5=EC=9D=84=20=EC=A0=9C?= =?UTF-8?q?=EC=99=B8=ED=95=9C=20=EA=B8=B0=EB=8A=A5=20=EA=B5=AC=ED=98=84=20?= =?UTF-8?q?=EC=99=84=EB=A3=8C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../kw/zeropick/product/domain/QProduct.java | 2 + .../kw/zeropick/review/domain/QReview.java | 5 ++ .../zeropick/review/domain/QReviewLike.java | 62 ++++++++++++++++++ .../product/controller/ProductController.java | 2 +- .../kw/zeropick/product/domain/Product.java | 2 + .../product/dto/ArtificialSweetDto.java | 18 ++++++ .../kw/zeropick/product/dto/ProductDto.java | 14 +++++ .../zeropick/product/dto/ReviewInfoDto.java | 20 ++++++ .../kw/zeropick/product/dto/ReviewTagDto.java | 22 +++++++ .../dto/request/ProductSearchRequest.java | 1 + .../ProductQueryDslRepositoryImpl.java | 5 ++ .../product/service/ProductServiceImpl.java | 55 +++++++++++++++- .../review/controller/ReviewController.java | 46 ++++++++++++++ .../kw/zeropick/review/domain/Review.java | 12 +++- .../kw/zeropick/review/domain/ReviewLike.java | 32 ++++++++++ .../review/dto/response/ReviewResponse.java | 4 ++ .../repository/ReviewJpaRepository.java | 2 + .../repository/ReviewLikeJpaRepository.java | 16 +++++ .../repository/ReviewTagJpaRepository.java | 2 + .../review/service/ReviewService.java | 5 ++ .../review/service/ReviewServiceImpl.java | 63 ++++++++++++++++++- 21 files changed, 385 insertions(+), 5 deletions(-) create mode 100644 src/main/generated/kw/zeropick/review/domain/QReviewLike.java create mode 100644 src/main/java/kw/zeropick/product/dto/ArtificialSweetDto.java create mode 100644 src/main/java/kw/zeropick/product/dto/ReviewInfoDto.java create mode 100644 src/main/java/kw/zeropick/product/dto/ReviewTagDto.java create mode 100644 src/main/java/kw/zeropick/review/domain/ReviewLike.java create mode 100644 src/main/java/kw/zeropick/review/repository/ReviewLikeJpaRepository.java diff --git a/src/main/generated/kw/zeropick/product/domain/QProduct.java b/src/main/generated/kw/zeropick/product/domain/QProduct.java index eb003ec..147c0cf 100644 --- a/src/main/generated/kw/zeropick/product/domain/QProduct.java +++ b/src/main/generated/kw/zeropick/product/domain/QProduct.java @@ -26,6 +26,8 @@ public class QProduct extends EntityPathBase { public final StringPath bigCategory = createString("bigCategory"); + public final BooleanPath bloodSugar = createBoolean("bloodSugar"); + public final NumberPath bookmarkCount = createNumber("bookmarkCount", Integer.class); public final StringPath brand = createString("brand"); diff --git a/src/main/generated/kw/zeropick/review/domain/QReview.java b/src/main/generated/kw/zeropick/review/domain/QReview.java index 7c46872..ebcae18 100644 --- a/src/main/generated/kw/zeropick/review/domain/QReview.java +++ b/src/main/generated/kw/zeropick/review/domain/QReview.java @@ -33,6 +33,10 @@ public class QReview extends EntityPathBase { public final ListPath imageUrls = this.createList("imageUrls", String.class, StringPath.class, PathInits.DIRECT2); + public final NumberPath likeCount = createNumber("likeCount", Long.class); + + public final kw.zeropick.member.domain.QMember member; + public final kw.zeropick.product.domain.QProduct product; public final NumberPath rating = createNumber("rating", Long.class); @@ -60,6 +64,7 @@ public QReview(PathMetadata metadata, PathInits inits) { public QReview(Class type, PathMetadata metadata, PathInits inits) { super(type, metadata, inits); + this.member = inits.isInitialized("member") ? new kw.zeropick.member.domain.QMember(forProperty("member")) : null; this.product = inits.isInitialized("product") ? new kw.zeropick.product.domain.QProduct(forProperty("product"), inits.get("product")) : null; } diff --git a/src/main/generated/kw/zeropick/review/domain/QReviewLike.java b/src/main/generated/kw/zeropick/review/domain/QReviewLike.java new file mode 100644 index 0000000..93116e5 --- /dev/null +++ b/src/main/generated/kw/zeropick/review/domain/QReviewLike.java @@ -0,0 +1,62 @@ +package kw.zeropick.review.domain; + +import static com.querydsl.core.types.PathMetadataFactory.*; + +import com.querydsl.core.types.dsl.*; + +import com.querydsl.core.types.PathMetadata; +import javax.annotation.processing.Generated; +import com.querydsl.core.types.Path; +import com.querydsl.core.types.dsl.PathInits; + + +/** + * QReviewLike is a Querydsl query type for ReviewLike + */ +@Generated("com.querydsl.codegen.DefaultEntitySerializer") +public class QReviewLike extends EntityPathBase { + + private static final long serialVersionUID = 1136379142L; + + private static final PathInits INITS = PathInits.DIRECT2; + + public static final QReviewLike reviewLike = new QReviewLike("reviewLike"); + + public final kw.zeropick.common.domain.QBaseEntity _super = new kw.zeropick.common.domain.QBaseEntity(this); + + //inherited + public final DateTimePath createdAt = _super.createdAt; + + public final NumberPath id = createNumber("id", Long.class); + + public final kw.zeropick.member.domain.QMember member; + + public final QReview review; + + //inherited + public final DateTimePath updatedAt = _super.updatedAt; + + public QReviewLike(String variable) { + this(ReviewLike.class, forVariable(variable), INITS); + } + + public QReviewLike(Path path) { + this(path.getType(), path.getMetadata(), PathInits.getFor(path.getMetadata(), INITS)); + } + + public QReviewLike(PathMetadata metadata) { + this(metadata, PathInits.getFor(metadata, INITS)); + } + + public QReviewLike(PathMetadata metadata, PathInits inits) { + this(ReviewLike.class, metadata, inits); + } + + public QReviewLike(Class type, PathMetadata metadata, PathInits inits) { + super(type, metadata, inits); + this.member = inits.isInitialized("member") ? new kw.zeropick.member.domain.QMember(forProperty("member")) : null; + this.review = inits.isInitialized("review") ? new QReview(forProperty("review"), inits.get("review")) : null; + } + +} + diff --git a/src/main/java/kw/zeropick/product/controller/ProductController.java b/src/main/java/kw/zeropick/product/controller/ProductController.java index 7b0ff53..76ccc8b 100644 --- a/src/main/java/kw/zeropick/product/controller/ProductController.java +++ b/src/main/java/kw/zeropick/product/controller/ProductController.java @@ -51,7 +51,7 @@ public ResponseEntity productDetail(@PathVariable Long productId) { } } -// 검색기능(태그 검색 보류) +// 검색기능 @GetMapping("/search") public ResponseEntity search(ProductSearchRequest request, @RequestParam(defaultValue = "0") int page, // 현재 페이지 diff --git a/src/main/java/kw/zeropick/product/domain/Product.java b/src/main/java/kw/zeropick/product/domain/Product.java index cb1fe1a..396e9f0 100644 --- a/src/main/java/kw/zeropick/product/domain/Product.java +++ b/src/main/java/kw/zeropick/product/domain/Product.java @@ -34,6 +34,8 @@ public class Product extends BaseEntity { private Boolean zeroKcal; + private Boolean bloodSugar; + private int price; private Double starRate; diff --git a/src/main/java/kw/zeropick/product/dto/ArtificialSweetDto.java b/src/main/java/kw/zeropick/product/dto/ArtificialSweetDto.java new file mode 100644 index 0000000..c5ef07e --- /dev/null +++ b/src/main/java/kw/zeropick/product/dto/ArtificialSweetDto.java @@ -0,0 +1,18 @@ +package kw.zeropick.product.dto; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Getter; + +@Getter +@AllArgsConstructor +@Builder +public class ArtificialSweetDto { + private String sweetName; + + private String sweetDetail; + + private String sweetPoint; + + private String sweetWarning; +} diff --git a/src/main/java/kw/zeropick/product/dto/ProductDto.java b/src/main/java/kw/zeropick/product/dto/ProductDto.java index 6e71190..2a3ed00 100644 --- a/src/main/java/kw/zeropick/product/dto/ProductDto.java +++ b/src/main/java/kw/zeropick/product/dto/ProductDto.java @@ -2,6 +2,8 @@ import jakarta.persistence.EnumType; import jakarta.persistence.Enumerated; +import java.util.ArrayList; +import java.util.List; import kw.zeropick.product.domain.Category; import lombok.AllArgsConstructor; import lombok.Builder; @@ -38,6 +40,18 @@ public class ProductDto { private IngredientDto ingredient; +// 감미료 정보 + @Setter + private List artificialSweets = new ArrayList<>(); + +// 해당상품 태그들 + @Setter + private List reviewTags = new ArrayList<>(); + +// 별점 통계 + @Setter + private ReviewInfoDto reviewInfo; + // 좋아요 여부 @Setter private Boolean bookmarked; diff --git a/src/main/java/kw/zeropick/product/dto/ReviewInfoDto.java b/src/main/java/kw/zeropick/product/dto/ReviewInfoDto.java new file mode 100644 index 0000000..6be18d7 --- /dev/null +++ b/src/main/java/kw/zeropick/product/dto/ReviewInfoDto.java @@ -0,0 +1,20 @@ +package kw.zeropick.product.dto; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Getter; + +@Getter +@AllArgsConstructor +@Builder +public class ReviewInfoDto { + private int oneStar; + + private int twoStar; + + private int threeStar; + + private int fourStar; + + private int fiveStar; +} diff --git a/src/main/java/kw/zeropick/product/dto/ReviewTagDto.java b/src/main/java/kw/zeropick/product/dto/ReviewTagDto.java new file mode 100644 index 0000000..2264baf --- /dev/null +++ b/src/main/java/kw/zeropick/product/dto/ReviewTagDto.java @@ -0,0 +1,22 @@ +package kw.zeropick.product.dto; + +import jakarta.persistence.EnumType; +import jakarta.persistence.Enumerated; +import kw.zeropick.review.domain.NegativeTagEnum; +import kw.zeropick.review.domain.PositiveTagEnum; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Getter; + +@Getter +@AllArgsConstructor +@Builder +public class ReviewTagDto { + private Integer tagCount; + + private PositiveTagEnum positiveTagEnum; + + private NegativeTagEnum negativeTagEnum; + + private Boolean positiveNegative; +} diff --git a/src/main/java/kw/zeropick/product/dto/request/ProductSearchRequest.java b/src/main/java/kw/zeropick/product/dto/request/ProductSearchRequest.java index 8445ccc..d2e3919 100644 --- a/src/main/java/kw/zeropick/product/dto/request/ProductSearchRequest.java +++ b/src/main/java/kw/zeropick/product/dto/request/ProductSearchRequest.java @@ -13,6 +13,7 @@ public class ProductSearchRequest { private String keyword; private Boolean zeroSugar; private Boolean zeroKcal; + private Boolean bloodSugar; private Boolean exceptErythritol; private Boolean exceptAllulose; private List tags; // 태그 검색 조건 diff --git a/src/main/java/kw/zeropick/product/repository/ProductQueryDslRepositoryImpl.java b/src/main/java/kw/zeropick/product/repository/ProductQueryDslRepositoryImpl.java index 4c78b30..05753c5 100644 --- a/src/main/java/kw/zeropick/product/repository/ProductQueryDslRepositoryImpl.java +++ b/src/main/java/kw/zeropick/product/repository/ProductQueryDslRepositoryImpl.java @@ -50,6 +50,11 @@ public Page searchProducts(ProductSearchRequest request, Pageable pagea builder.and(product.zeroKcal.eq(request.getZeroKcal())); } + // bloodSugar 조건 + if (request.getBloodSugar() != null) { + builder.and(product.bloodSugar.eq(request.getBloodSugar())); + } + // 인공감미료 조건 if (Boolean.TRUE.equals(request.getExceptErythritol())) { builder.and(product.ingredient.erythritol.isNull()); diff --git a/src/main/java/kw/zeropick/product/service/ProductServiceImpl.java b/src/main/java/kw/zeropick/product/service/ProductServiceImpl.java index e970cdd..2864508 100644 --- a/src/main/java/kw/zeropick/product/service/ProductServiceImpl.java +++ b/src/main/java/kw/zeropick/product/service/ProductServiceImpl.java @@ -1,6 +1,7 @@ package kw.zeropick.product.service; import jakarta.persistence.EntityNotFoundException; +import java.util.ArrayList; import java.util.List; import kw.zeropick.member.domain.Member; import kw.zeropick.member.repository.MemberJpaRepository; @@ -8,12 +9,18 @@ import kw.zeropick.product.domain.Compare; import kw.zeropick.product.domain.Ingredient; import kw.zeropick.product.domain.Product; +import kw.zeropick.product.dto.ArtificialSweetDto; import kw.zeropick.product.dto.IngredientDto; import kw.zeropick.product.dto.ProductDto; +import kw.zeropick.product.dto.ReviewInfoDto; +import kw.zeropick.product.dto.ReviewTagDto; import kw.zeropick.product.dto.request.ProductSearchRequest; import kw.zeropick.product.repository.BookmarkJpaRepository; import kw.zeropick.product.repository.CompareJpaRepository; import kw.zeropick.product.repository.ProductJpaRepository; +import kw.zeropick.review.domain.ReviewTag; +import kw.zeropick.review.repository.ReviewJpaRepository; +import kw.zeropick.review.repository.ReviewTagJpaRepository; import lombok.Builder; import lombok.RequiredArgsConstructor; import org.springframework.data.domain.Page; @@ -31,11 +38,13 @@ public class ProductServiceImpl implements ProductService{ private final BookmarkJpaRepository bookmarkJpaRepository; private final MemberJpaRepository memberJpaRepository; + private final ReviewJpaRepository reviewJpaRepository; + private final ReviewTagJpaRepository reviewTagJpaRepository; @Override @Transactional public ProductDto productDetail(Long productId) { -// 찜 여부와 비교를 사용하려면 회원이여야 하는데 그럼 상세 정보 기능은 회원과 비회원으로 나누어야 하는가? +// 찜 여부와 비교를 사용하기 위해 회원 적용시 수정 필요 Product product = productJpaRepository.findById(productId) .orElseThrow(() -> new EntityNotFoundException("해당 id에 맞는 상품 없음 id: " + productId)); @@ -43,6 +52,50 @@ public ProductDto productDetail(Long productId) { productJpaRepository.save(product); ProductDto productDto = toProductDto(product, null); + List artificialSweetDtos = new ArrayList<>(); + if(product.getIngredient().getAllulose() != 0){ + ArtificialSweetDto artificialSweetDto = ArtificialSweetDto.builder() + .sweetName("알룰로오스") + .sweetDetail("알룰로오스 설명") + .sweetPoint("알룰로오스 특징") + .sweetWarning("알룰로오스 경고") + .build(); + artificialSweetDtos.add(artificialSweetDto); + } + if(product.getIngredient().getErythritol() != 0){ + ArtificialSweetDto artificialSweetDto = ArtificialSweetDto.builder() + .sweetName("에리트리톨") + .sweetDetail("에리트리톨 설명") + .sweetPoint("에리트리톨 특징") + .sweetWarning("에리트리톨 경고") + .build(); + artificialSweetDtos.add(artificialSweetDto); + } + productDto.setArtificialSweets(artificialSweetDtos); + + // 상품에 대한 리뷰 태그 정보 + List reviewTags = reviewTagJpaRepository.findAllByProductId(productId); + List reviewTagDtos = reviewTags.stream() + .map(rt -> ReviewTagDto.builder() + .tagCount(rt.getTagCount()) + .positiveTagEnum(rt.getPositiveTagEnum()) + .negativeTagEnum(rt.getNegativeTagEnum()) + .positiveNegative(rt.getPositiveNegative()) + .build()) + .toList(); + + productDto.setReviewTags(reviewTagDtos); + + // 별점 정보 + ReviewInfoDto reviewInfoDto = ReviewInfoDto.builder() + .oneStar(reviewJpaRepository.countByProductIdAndRating(productId, 1L).intValue()) + .twoStar(reviewJpaRepository.countByProductIdAndRating(productId, 2L).intValue()) + .threeStar(reviewJpaRepository.countByProductIdAndRating(productId, 3L).intValue()) + .fourStar(reviewJpaRepository.countByProductIdAndRating(productId, 4L).intValue()) + .fiveStar(reviewJpaRepository.countByProductIdAndRating(productId, 5L).intValue()) + .build(); + + productDto.setReviewInfo(reviewInfoDto); return productDto; } diff --git a/src/main/java/kw/zeropick/review/controller/ReviewController.java b/src/main/java/kw/zeropick/review/controller/ReviewController.java index 70f239c..4294169 100644 --- a/src/main/java/kw/zeropick/review/controller/ReviewController.java +++ b/src/main/java/kw/zeropick/review/controller/ReviewController.java @@ -3,6 +3,7 @@ import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.tags.Tag; import java.util.List; +import kw.zeropick.common.LoginUser; import kw.zeropick.payload.ApiResponse; import kw.zeropick.product.dto.ProductDto; import kw.zeropick.review.domain.PositiveTagEnum; @@ -77,4 +78,49 @@ public ResponseEntity getReviews( ); } } + // 리뷰 좋아요 하기 + @Operation(summary = "리뷰 좋아요 하기", description = "리뷰 좋아요 추가 요청(로그인 기능 적용 전이므로 1번유저 고정)") + @PostMapping("/likeReview/{reviewId}") + public ResponseEntity productCompare(@PathVariable Long reviewId) { + Long memberId = LoginUser.get().getId(); + try { + reviewService.reviewLike(reviewId, memberId); + return ResponseEntity.ok( + ApiResponse.builder() + .check(true) + .information("Review like successfully") + .build() + ); + } catch (Exception e) { + return ResponseEntity.internalServerError().body( + ApiResponse.builder() + .check(false) + .information(e.getMessage()) + .build() + ); + } + } + + // 리뷰 좋아요 취소 + @Operation(summary = "리뷰 좋아요 취소", description = "리뷰 좋아요 취소 요청(로그인 기능 적용 전이므로 1번유저 고정)") + @DeleteMapping("/likeReview/{reviewId}") + public ResponseEntity productCompareUndo(@PathVariable Long reviewId) { + Long memberId = LoginUser.get().getId(); + try { + reviewService.undoReviewLike(reviewId, memberId); + return ResponseEntity.ok( + ApiResponse.builder() + .check(true) + .information("Review like deleted successfully") + .build() + ); + } catch (Exception e) { + return ResponseEntity.internalServerError().body( + ApiResponse.builder() + .check(false) + .information(e.getMessage()) + .build() + ); + } + } } diff --git a/src/main/java/kw/zeropick/review/domain/Review.java b/src/main/java/kw/zeropick/review/domain/Review.java index 4e06e94..734a4ab 100644 --- a/src/main/java/kw/zeropick/review/domain/Review.java +++ b/src/main/java/kw/zeropick/review/domain/Review.java @@ -5,6 +5,7 @@ import java.util.ArrayList; import kw.zeropick.common.converter.StringListToStringConverter; import kw.zeropick.common.domain.BaseEntity; +import kw.zeropick.member.domain.Member; import kw.zeropick.product.domain.Product; import lombok.AllArgsConstructor; import lombok.Builder; @@ -12,6 +13,7 @@ import lombok.NoArgsConstructor; import java.util.List; +import lombok.Setter; @Entity @Getter @@ -28,12 +30,19 @@ public class Review extends BaseEntity { @JoinColumn(name = "product_id", nullable = false) private Product product; + @ManyToOne(fetch = FetchType.LAZY) + @JoinColumn(name = "member_id", nullable = false) // 리뷰 작성자 + private Member member; + @NotNull private Long rating; @NotNull private String content; + @Setter + private Long likeCount; + @Convert(converter = StringListToStringConverter.class) private List imageUrls = new ArrayList<>(); @@ -59,6 +68,5 @@ public void setContent(String content) { public void setImageUrls(List imageUrls) { this.imageUrls = imageUrls; } - - } + diff --git a/src/main/java/kw/zeropick/review/domain/ReviewLike.java b/src/main/java/kw/zeropick/review/domain/ReviewLike.java new file mode 100644 index 0000000..578bb62 --- /dev/null +++ b/src/main/java/kw/zeropick/review/domain/ReviewLike.java @@ -0,0 +1,32 @@ +package kw.zeropick.review.domain; + +import jakarta.persistence.*; +import kw.zeropick.common.domain.BaseEntity; +import kw.zeropick.member.domain.Member; +import kw.zeropick.product.domain.Product; +import lombok.*; + +@Entity +@Getter +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class ReviewLike extends BaseEntity { + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + @Column(name = "review_like_id") + private Long id; + + @ManyToOne(fetch = FetchType.LAZY) + @JoinColumn(name = "review_id", nullable = false) + private Review review; + + @ManyToOne(fetch = FetchType.LAZY) + @JoinColumn(name = "member_id", nullable = false) + private Member member; + + public ReviewLike(Member member, Review review) { + this.member = member; + this.review = review; + } +} diff --git a/src/main/java/kw/zeropick/review/dto/response/ReviewResponse.java b/src/main/java/kw/zeropick/review/dto/response/ReviewResponse.java index d80052c..888c0a8 100644 --- a/src/main/java/kw/zeropick/review/dto/response/ReviewResponse.java +++ b/src/main/java/kw/zeropick/review/dto/response/ReviewResponse.java @@ -24,9 +24,13 @@ public class ReviewResponse { private String content; + private Long likeCount; + private List imageUrls = new ArrayList<>(); private List positiveTags = new ArrayList<>(); private List negativeTags = new ArrayList<>(); + + private Boolean myReview; } diff --git a/src/main/java/kw/zeropick/review/repository/ReviewJpaRepository.java b/src/main/java/kw/zeropick/review/repository/ReviewJpaRepository.java index 6b03a2c..c722488 100644 --- a/src/main/java/kw/zeropick/review/repository/ReviewJpaRepository.java +++ b/src/main/java/kw/zeropick/review/repository/ReviewJpaRepository.java @@ -8,4 +8,6 @@ import org.springframework.data.repository.query.Param; public interface ReviewJpaRepository extends JpaRepository, ReviewQueryDslRepository { + @Query("SELECT COUNT(r) FROM Review r WHERE r.product.id = :productId AND r.rating = :rating") + Long countByProductIdAndRating(Long productId, Long rating); } diff --git a/src/main/java/kw/zeropick/review/repository/ReviewLikeJpaRepository.java b/src/main/java/kw/zeropick/review/repository/ReviewLikeJpaRepository.java new file mode 100644 index 0000000..0b6c965 --- /dev/null +++ b/src/main/java/kw/zeropick/review/repository/ReviewLikeJpaRepository.java @@ -0,0 +1,16 @@ +package kw.zeropick.review.repository; + +import java.util.List; +import java.util.Optional; +import kw.zeropick.member.domain.Member; +import kw.zeropick.review.domain.Review; +import kw.zeropick.review.domain.ReviewLike; +import org.springframework.data.jpa.repository.JpaRepository; + +public interface ReviewLikeJpaRepository extends JpaRepository { + Boolean existsByReviewAndMember(Review review, Member member); + Optional findByReviewAndMember(Review review, Member member); + List findAllByMemberId(Long memberId); + Boolean existsByReviewIdAndMemberId(Long reviewId, Long memberId); + Long countByReview(Review review); +} diff --git a/src/main/java/kw/zeropick/review/repository/ReviewTagJpaRepository.java b/src/main/java/kw/zeropick/review/repository/ReviewTagJpaRepository.java index a02cac4..b3b8110 100644 --- a/src/main/java/kw/zeropick/review/repository/ReviewTagJpaRepository.java +++ b/src/main/java/kw/zeropick/review/repository/ReviewTagJpaRepository.java @@ -1,5 +1,6 @@ package kw.zeropick.review.repository; +import java.util.List; import java.util.Optional; import kw.zeropick.review.domain.NegativeTagEnum; import kw.zeropick.review.domain.PositiveTagEnum; @@ -12,4 +13,5 @@ public interface ReviewTagJpaRepository extends JpaRepository { @Query("SELECT rt FROM ReviewTag rt WHERE rt.productId = :productId AND ((:positiveTagEnum IS NOT NULL AND rt.positiveTagEnum = :positiveTagEnum) OR (:negativeTagEnum IS NOT NULL AND rt.negativeTagEnum = :negativeTagEnum))") Optional findByProductIdAndTag(@Param("productId") Long productId, @Param("positiveTagEnum") PositiveTagEnum positiveTagEnum, @Param("negativeTagEnum") NegativeTagEnum negativeTagEnum); + List findAllByProductId(Long productId); } diff --git a/src/main/java/kw/zeropick/review/service/ReviewService.java b/src/main/java/kw/zeropick/review/service/ReviewService.java index c0a1788..404411b 100644 --- a/src/main/java/kw/zeropick/review/service/ReviewService.java +++ b/src/main/java/kw/zeropick/review/service/ReviewService.java @@ -1,6 +1,7 @@ package kw.zeropick.review.service; import java.util.List; +import kw.zeropick.product.dto.ProductDto; import kw.zeropick.review.domain.PositiveTagEnum; import kw.zeropick.review.domain.Review; import kw.zeropick.review.dto.request.ReviewRequestDto; @@ -14,4 +15,8 @@ public interface ReviewService { void createReview(ReviewRequestDto reviewRequestDto, List files); void updateReview(Long reviewId, ReviewRequestDto reviewRequestDto, List files); void deleteReview(Long reviewId); + + public void reviewLike(Long reviewId, Long memberId); + public void undoReviewLike(Long reviewId, Long memberId); + public Page bookmarkProductList(Long memberId, int page, int size); } diff --git a/src/main/java/kw/zeropick/review/service/ReviewServiceImpl.java b/src/main/java/kw/zeropick/review/service/ReviewServiceImpl.java index 5cf11ec..24d0d6e 100644 --- a/src/main/java/kw/zeropick/review/service/ReviewServiceImpl.java +++ b/src/main/java/kw/zeropick/review/service/ReviewServiceImpl.java @@ -7,15 +7,20 @@ import java.util.stream.Collectors; import kw.zeropick.common.domain.exception.ResourceNotFoundException; +import kw.zeropick.member.domain.Member; +import kw.zeropick.member.repository.MemberJpaRepository; +import kw.zeropick.product.domain.Compare; import kw.zeropick.product.domain.Product; import kw.zeropick.product.repository.ProductJpaRepository; import kw.zeropick.review.domain.PositiveTagEnum; import kw.zeropick.review.domain.Review; +import kw.zeropick.review.domain.ReviewLike; import kw.zeropick.review.domain.ReviewTag; import kw.zeropick.review.domain.ReviewTagMapping; import kw.zeropick.review.dto.request.ReviewRequestDto; import kw.zeropick.review.dto.response.ReviewResponse; import kw.zeropick.review.repository.ReviewJpaRepository; +import kw.zeropick.review.repository.ReviewLikeJpaRepository; import kw.zeropick.review.repository.ReviewTagJpaRepository; import kw.zeropick.review.repository.ReviewTagMappingJpaRepository; import kw.zeropick.util.S3Util; @@ -30,17 +35,31 @@ @Transactional(readOnly = true) @RequiredArgsConstructor public class ReviewServiceImpl implements ReviewService { + private final MemberJpaRepository memberJpaRepository; private final ReviewJpaRepository reviewRepository; private final ProductJpaRepository productRepository; private final ReviewTagJpaRepository reviewTagRepository; private final ReviewTagMappingJpaRepository reviewTagMappingRepository; + private final ReviewLikeJpaRepository reviewLikeJpaRepository; private final S3Util s3Util; public Page getReviews(Long productId, PositiveTagEnum positiveTag, String sort, Pageable pageable) { - return reviewRepository.findReviewsByProductId(productId, positiveTag, sort, pageable); + Long currentMemberId = 1L; // 로그인 적용 전으로 1L 고정 + Member currentMember = memberJpaRepository.findById(currentMemberId) + .orElseThrow(() -> new IllegalArgumentException("해당 멤버를 찾을 수 없습니다.")); + + Page reviewsByProductId = reviewRepository.findReviewsByProductId(productId, positiveTag, sort, pageable); + + // myReview 값 설정 + return reviewsByProductId.map(reviewResponse -> { + boolean isMyReview = reviewResponse.getUserName() != null && currentMember.getName().equals(reviewResponse.getUserName()); + reviewResponse.setMyReview(isMyReview); + return reviewResponse; + }); } + @Override @Transactional public void createReview(ReviewRequestDto reviewRequestDto, List files) { @@ -275,5 +294,47 @@ public void deleteReview(Long reviewId) { productRepository.save(product); } + @Override + public Page bookmarkProductList(Long memberId, int page, int size) { +// 좋아요한 리뷰 보류 + return null; + } + + @Override + @Transactional + public void reviewLike(Long reviewId, Long memberId) { + Review review = reviewRepository.findById(reviewId) + .orElseThrow(() -> new EntityNotFoundException("해당 id에 맞는 리뷰 없음 id: " + reviewId)); + + Member member = memberJpaRepository.findById(memberId) + .orElseThrow(() -> new EntityNotFoundException("해당 id에 맞는 유저 없음 id: " + memberId)); + + if(reviewLikeJpaRepository.existsByReviewAndMember(review, member)) { + throw new RuntimeException("이미 좋아요한 리뷰입니다."); + } + + review.setLikeCount(review.getLikeCount() + 1); + reviewRepository.save(review); + + ReviewLike reviewLike = new ReviewLike(member, review); + reviewLikeJpaRepository.save(reviewLike); + } + + @Override + @Transactional + public void undoReviewLike(Long reviewId, Long memberId) { + Review review = reviewRepository.findById(reviewId) + .orElseThrow(() -> new EntityNotFoundException("해당 id에 맞는 리뷰 없음 id: " + reviewId)); + + Member member = memberJpaRepository.findById(memberId) + .orElseThrow(() -> new EntityNotFoundException("해당 id에 맞는 유저 없음 id: " + memberId)); + + ReviewLike reviewLike = reviewLikeJpaRepository.findByReviewAndMember(review, member) + .orElseThrow(() -> new EntityNotFoundException("해당 리뷰는 좋아요 목록에 없습니다.")); + review.setLikeCount(review.getLikeCount() - 1); + reviewRepository.save(review); + + reviewLikeJpaRepository.delete(reviewLike); + } } From fd69548d52556ab5477e3124de3d45bfdb242566 Mon Sep 17 00:00:00 2001 From: inswal843 Date: Tue, 28 Jan 2025 14:57:44 +0900 Subject: [PATCH 2/6] =?UTF-8?q?[Feat]=20=EB=A6=AC=EB=B7=B0=20=EC=A2=8B?= =?UTF-8?q?=EC=95=84=EC=9A=94=20=EA=B0=AF=EC=88=98=20=EB=B0=98=ED=99=98=20?= =?UTF-8?q?=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/kw/zeropick/product/controller/ProductController.java | 3 ++- .../review/repository/ReviewQueryDslRepositoryImpl.java | 1 + 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/src/main/java/kw/zeropick/product/controller/ProductController.java b/src/main/java/kw/zeropick/product/controller/ProductController.java index 76ccc8b..e5f78a3 100644 --- a/src/main/java/kw/zeropick/product/controller/ProductController.java +++ b/src/main/java/kw/zeropick/product/controller/ProductController.java @@ -51,7 +51,8 @@ public ResponseEntity productDetail(@PathVariable Long productId) { } } -// 검색기능 +// 검색 기능 + @Operation(summary = "상품 검색 기능", description = "상품 검색 기능(로그인 기능 적용 전이므로 1번유저 고정)") @GetMapping("/search") public ResponseEntity search(ProductSearchRequest request, @RequestParam(defaultValue = "0") int page, // 현재 페이지 diff --git a/src/main/java/kw/zeropick/review/repository/ReviewQueryDslRepositoryImpl.java b/src/main/java/kw/zeropick/review/repository/ReviewQueryDslRepositoryImpl.java index 0ed12a1..b7f0385 100644 --- a/src/main/java/kw/zeropick/review/repository/ReviewQueryDslRepositoryImpl.java +++ b/src/main/java/kw/zeropick/review/repository/ReviewQueryDslRepositoryImpl.java @@ -88,6 +88,7 @@ private ReviewResponse mapToResponse(Review review) { .userName("User") // 실제 사용자 정보를 가져오는 로직으로 대체 .rating(review.getRating()) .content(review.getContent()) + .likeCount(review.getLikeCount()) .imageUrls(review.getImageUrls()) .positiveTags(positiveTags) .negativeTags(negativeTags) From 6135ed2a0e86c5b05dcb5c18006a3f7e3fc995c3 Mon Sep 17 00:00:00 2001 From: Suhun0331 Date: Thu, 30 Jan 2025 18:12:18 +0900 Subject: [PATCH 3/6] =?UTF-8?q?Feat=20:=20=EB=B6=80=EC=A0=95=ED=83=9C?= =?UTF-8?q?=EA=B7=B8=20=EC=83=81=EC=9C=84=203=EA=B0=9C=20=EC=A0=80?= =?UTF-8?q?=EC=9E=A5=20=EB=A1=9C=EC=A7=81=20=EC=B6=94=EA=B0=80(=EB=B6=80?= =?UTF-8?q?=EC=A0=95=ED=83=9C=EA=B7=B8=20=ED=83=80=EC=9E=85=20=EC=9E=84?= =?UTF-8?q?=EC=8B=9C=20a=20b=20c=20=EC=B6=94=EA=B0=80)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../kw/zeropick/product/domain/QProduct.java | 6 +- .../zeropick/review/domain/QReviewLike.java | 124 +++++------ .../kw/zeropick/product/domain/Product.java | 16 +- .../ProductQueryDslRepositoryImpl.java | 2 +- .../review/controller/ReviewController.java | 3 +- .../review/domain/NegativeTagEnum.java | 5 +- .../review/service/ReviewService.java | 2 +- .../review/service/ReviewServiceImpl.java | 195 +++++------------- 8 files changed, 133 insertions(+), 220 deletions(-) diff --git a/src/main/generated/kw/zeropick/product/domain/QProduct.java b/src/main/generated/kw/zeropick/product/domain/QProduct.java index 147c0cf..dcba5de 100644 --- a/src/main/generated/kw/zeropick/product/domain/QProduct.java +++ b/src/main/generated/kw/zeropick/product/domain/QProduct.java @@ -45,8 +45,12 @@ public class QProduct extends EntityPathBase { public final QIngredient ingredient; + public final ListPath> negativeTop3tags = this.>createList("negativeTop3tags", kw.zeropick.review.domain.NegativeTagEnum.class, EnumPath.class, PathInits.DIRECT2); + public final NumberPath popularity = createNumber("popularity", Integer.class); + public final ListPath> positiveTop3tags = this.>createList("positiveTop3tags", kw.zeropick.review.domain.PositiveTagEnum.class, EnumPath.class, PathInits.DIRECT2); + public final NumberPath price = createNumber("price", Integer.class); public final StringPath productName = createString("productName"); @@ -57,8 +61,6 @@ public class QProduct extends EntityPathBase { public final NumberPath starRate = createNumber("starRate", Double.class); - public final ListPath> tags = this.>createList("tags", kw.zeropick.review.domain.PositiveTagEnum.class, EnumPath.class, PathInits.DIRECT2); - //inherited public final DateTimePath updatedAt = _super.updatedAt; diff --git a/src/main/generated/kw/zeropick/review/domain/QReviewLike.java b/src/main/generated/kw/zeropick/review/domain/QReviewLike.java index 93116e5..34da5e5 100644 --- a/src/main/generated/kw/zeropick/review/domain/QReviewLike.java +++ b/src/main/generated/kw/zeropick/review/domain/QReviewLike.java @@ -1,62 +1,62 @@ -package kw.zeropick.review.domain; - -import static com.querydsl.core.types.PathMetadataFactory.*; - -import com.querydsl.core.types.dsl.*; - -import com.querydsl.core.types.PathMetadata; -import javax.annotation.processing.Generated; -import com.querydsl.core.types.Path; -import com.querydsl.core.types.dsl.PathInits; - - -/** - * QReviewLike is a Querydsl query type for ReviewLike - */ -@Generated("com.querydsl.codegen.DefaultEntitySerializer") -public class QReviewLike extends EntityPathBase { - - private static final long serialVersionUID = 1136379142L; - - private static final PathInits INITS = PathInits.DIRECT2; - - public static final QReviewLike reviewLike = new QReviewLike("reviewLike"); - - public final kw.zeropick.common.domain.QBaseEntity _super = new kw.zeropick.common.domain.QBaseEntity(this); - - //inherited - public final DateTimePath createdAt = _super.createdAt; - - public final NumberPath id = createNumber("id", Long.class); - - public final kw.zeropick.member.domain.QMember member; - - public final QReview review; - - //inherited - public final DateTimePath updatedAt = _super.updatedAt; - - public QReviewLike(String variable) { - this(ReviewLike.class, forVariable(variable), INITS); - } - - public QReviewLike(Path path) { - this(path.getType(), path.getMetadata(), PathInits.getFor(path.getMetadata(), INITS)); - } - - public QReviewLike(PathMetadata metadata) { - this(metadata, PathInits.getFor(metadata, INITS)); - } - - public QReviewLike(PathMetadata metadata, PathInits inits) { - this(ReviewLike.class, metadata, inits); - } - - public QReviewLike(Class type, PathMetadata metadata, PathInits inits) { - super(type, metadata, inits); - this.member = inits.isInitialized("member") ? new kw.zeropick.member.domain.QMember(forProperty("member")) : null; - this.review = inits.isInitialized("review") ? new QReview(forProperty("review"), inits.get("review")) : null; - } - -} - +package kw.zeropick.review.domain; + +import static com.querydsl.core.types.PathMetadataFactory.*; + +import com.querydsl.core.types.dsl.*; + +import com.querydsl.core.types.PathMetadata; +import javax.annotation.processing.Generated; +import com.querydsl.core.types.Path; +import com.querydsl.core.types.dsl.PathInits; + + +/** + * QReviewLike is a Querydsl query type for ReviewLike + */ +@Generated("com.querydsl.codegen.DefaultEntitySerializer") +public class QReviewLike extends EntityPathBase { + + private static final long serialVersionUID = 1136379142L; + + private static final PathInits INITS = PathInits.DIRECT2; + + public static final QReviewLike reviewLike = new QReviewLike("reviewLike"); + + public final kw.zeropick.common.domain.QBaseEntity _super = new kw.zeropick.common.domain.QBaseEntity(this); + + //inherited + public final DateTimePath createdAt = _super.createdAt; + + public final NumberPath id = createNumber("id", Long.class); + + public final kw.zeropick.member.domain.QMember member; + + public final QReview review; + + //inherited + public final DateTimePath updatedAt = _super.updatedAt; + + public QReviewLike(String variable) { + this(ReviewLike.class, forVariable(variable), INITS); + } + + public QReviewLike(Path path) { + this(path.getType(), path.getMetadata(), PathInits.getFor(path.getMetadata(), INITS)); + } + + public QReviewLike(PathMetadata metadata) { + this(metadata, PathInits.getFor(metadata, INITS)); + } + + public QReviewLike(PathMetadata metadata, PathInits inits) { + this(ReviewLike.class, metadata, inits); + } + + public QReviewLike(Class type, PathMetadata metadata, PathInits inits) { + super(type, metadata, inits); + this.member = inits.isInitialized("member") ? new kw.zeropick.member.domain.QMember(forProperty("member")) : null; + this.review = inits.isInitialized("review") ? new QReview(forProperty("review"), inits.get("review")) : null; + } + +} + diff --git a/src/main/java/kw/zeropick/product/domain/Product.java b/src/main/java/kw/zeropick/product/domain/Product.java index 396e9f0..1e21493 100644 --- a/src/main/java/kw/zeropick/product/domain/Product.java +++ b/src/main/java/kw/zeropick/product/domain/Product.java @@ -7,6 +7,7 @@ import java.util.List; import kw.zeropick.common.converter.StringListToStringConverter; import kw.zeropick.common.domain.BaseEntity; +import kw.zeropick.review.domain.NegativeTagEnum; import kw.zeropick.review.domain.PositiveTagEnum; import kw.zeropick.review.domain.Review; import lombok.*; @@ -48,8 +49,13 @@ public class Product extends BaseEntity { private int reviewCount; + //상품 별 긍정태그 상위 3개 @Convert(converter = StringListToStringConverter.class) - private List tags; + private List positiveTop3tags; + + //상품 별 부정태그 상위 3개 + @Convert(converter = StringListToStringConverter.class) + private List negativeTop3tags; @OneToMany(mappedBy = "product", fetch = FetchType.LAZY) private List reviews = new ArrayList<>(); @@ -93,8 +99,12 @@ public void setReviewCount(int reviewCount) { this.reviewCount = reviewCount; this.setPopularity(); } - public void setTags(List tags) { - this.tags = tags; + public void setPositiveTop3tags(List positiveTop3tags) { + this.positiveTop3tags = positiveTop3tags; + } + + public void setNegativeTop3tags(List negativeTop3tags) { + this.negativeTop3tags = negativeTop3tags; } public void setPopularity() { diff --git a/src/main/java/kw/zeropick/product/repository/ProductQueryDslRepositoryImpl.java b/src/main/java/kw/zeropick/product/repository/ProductQueryDslRepositoryImpl.java index 05753c5..9d2dcbb 100644 --- a/src/main/java/kw/zeropick/product/repository/ProductQueryDslRepositoryImpl.java +++ b/src/main/java/kw/zeropick/product/repository/ProductQueryDslRepositoryImpl.java @@ -69,7 +69,7 @@ public Page searchProducts(ProductSearchRequest request, Pageable pagea for (PositiveTagEnum tag : request.getTags()) { tagsCondition.or(Expressions.stringTemplate( "cast({0} as text)", - product.tags + product.positiveTop3tags ).like("%" + tag.name() + "%")); } builder.and(tagsCondition); diff --git a/src/main/java/kw/zeropick/review/controller/ReviewController.java b/src/main/java/kw/zeropick/review/controller/ReviewController.java index 4294169..e7ef762 100644 --- a/src/main/java/kw/zeropick/review/controller/ReviewController.java +++ b/src/main/java/kw/zeropick/review/controller/ReviewController.java @@ -32,7 +32,8 @@ public class ReviewController { public ResponseEntity createReview( @RequestPart(value = "review") ReviewRequestDto reviewRequestDto, @RequestPart(value = "files", required = false) List files) { - reviewService.createReview(reviewRequestDto, files); + Long memberId = 1L; + reviewService.createReview(memberId, reviewRequestDto, files); return ResponseEntity.ok("리뷰가 성공적으로 등록되었습니다."); } diff --git a/src/main/java/kw/zeropick/review/domain/NegativeTagEnum.java b/src/main/java/kw/zeropick/review/domain/NegativeTagEnum.java index 179aeda..53e6007 100644 --- a/src/main/java/kw/zeropick/review/domain/NegativeTagEnum.java +++ b/src/main/java/kw/zeropick/review/domain/NegativeTagEnum.java @@ -2,5 +2,8 @@ public enum NegativeTagEnum { NOT_GOOD, - EXPENSIVE + EXPENSIVE, + A, + B, + C } diff --git a/src/main/java/kw/zeropick/review/service/ReviewService.java b/src/main/java/kw/zeropick/review/service/ReviewService.java index 404411b..e154ca4 100644 --- a/src/main/java/kw/zeropick/review/service/ReviewService.java +++ b/src/main/java/kw/zeropick/review/service/ReviewService.java @@ -12,7 +12,7 @@ public interface ReviewService { Page getReviews(Long productId, PositiveTagEnum positiveTag, String sort, Pageable pageable); - void createReview(ReviewRequestDto reviewRequestDto, List files); + void createReview(Long memberId, ReviewRequestDto reviewRequestDto, List files); void updateReview(Long reviewId, ReviewRequestDto reviewRequestDto, List files); void deleteReview(Long reviewId); diff --git a/src/main/java/kw/zeropick/review/service/ReviewServiceImpl.java b/src/main/java/kw/zeropick/review/service/ReviewServiceImpl.java index 24d0d6e..8fef0d9 100644 --- a/src/main/java/kw/zeropick/review/service/ReviewServiceImpl.java +++ b/src/main/java/kw/zeropick/review/service/ReviewServiceImpl.java @@ -2,6 +2,7 @@ import jakarta.persistence.EntityNotFoundException; import java.util.ArrayList; +import java.util.Comparator; import java.util.List; import java.util.Optional; import java.util.stream.Collectors; @@ -12,11 +13,7 @@ import kw.zeropick.product.domain.Compare; import kw.zeropick.product.domain.Product; import kw.zeropick.product.repository.ProductJpaRepository; -import kw.zeropick.review.domain.PositiveTagEnum; -import kw.zeropick.review.domain.Review; -import kw.zeropick.review.domain.ReviewLike; -import kw.zeropick.review.domain.ReviewTag; -import kw.zeropick.review.domain.ReviewTagMapping; +import kw.zeropick.review.domain.*; import kw.zeropick.review.dto.request.ReviewRequestDto; import kw.zeropick.review.dto.response.ReviewResponse; import kw.zeropick.review.repository.ReviewJpaRepository; @@ -59,13 +56,15 @@ public Page getReviews(Long productId, PositiveTagEnum positiveT }); } - @Override @Transactional - public void createReview(ReviewRequestDto reviewRequestDto, List files) { + public void createReview(Long memberId, ReviewRequestDto reviewRequestDto, List files) { Product product = productRepository.findById(reviewRequestDto.getProductId()) .orElseThrow(() -> new EntityNotFoundException("해당 상품을 찾을 수 없습니다.")); + Member member = memberJpaRepository.findById(memberId) + .orElseThrow(() -> new EntityNotFoundException("해당 회원을 찾을 수 없습니다.")); + double newStarRate = ((product.getStarRate() * product.getReviewCount()) + reviewRequestDto.getRating()) / (product.getReviewCount() + 1); product.setStarRate(newStarRate); product.setReviewCount(product.getReviewCount() + 1); @@ -86,59 +85,14 @@ public void createReview(ReviewRequestDto reviewRequestDto, List Review review = Review.builder() .product(product) + .member(member) .rating(reviewRequestDto.getRating()) .content(reviewRequestDto.getContent()) .imageUrls(images) .build(); reviewRepository.save(review); - reviewRequestDto.getPositiveTags().forEach(tag -> { - Optional existingTag = reviewTagRepository.findByProductIdAndTag(product.getId(), tag, null); - ReviewTag reviewTag; - if (existingTag.isPresent()) { - reviewTag = existingTag.get(); - reviewTag.setTagCount(reviewTag.getTagCount() + 1); - } else { - reviewTag = ReviewTag.builder() - .productId(product.getId()) - .positiveTagEnum(tag) - .tagCount(1) - .positiveNegative(true) - .build(); - } -// reviewTag.setReview(review); - reviewTagRepository.save(reviewTag); - reviewTagMappingRepository.save(new ReviewTagMapping(review, reviewTag)); - }); - - reviewRequestDto.getNegativeTags().forEach(tag -> { - Optional existingTag = reviewTagRepository.findByProductIdAndTag(product.getId(), null, tag); - ReviewTag reviewTag; - if (existingTag.isPresent()) { - reviewTag = existingTag.get(); - reviewTag.setTagCount(reviewTag.getTagCount() + 1); - } else { - reviewTag = ReviewTag.builder() - .productId(product.getId()) - .negativeTagEnum(tag) - .tagCount(1) - .positiveNegative(false) - .build(); - } -// reviewTag.setReview(review); - reviewTagRepository.save(reviewTag); - reviewTagMappingRepository.save(new ReviewTagMapping(review, reviewTag)); - }); - - List topTags = reviewTagRepository.findAll().stream() - .filter(tag -> tag.getPositiveNegative() && tag.getProductId() == product.getId()) - .sorted((t1, t2) -> t2.getTagCount().compareTo(t1.getTagCount())) - .limit(3) - .map(ReviewTag::getPositiveTagEnum) - .collect(Collectors.toList()); - - product.setTags(topTags); - productRepository.save(product); + saveReviewTags(reviewRequestDto, review, product); } @Override @@ -148,13 +102,10 @@ public void updateReview(Long reviewId, ReviewRequestDto reviewRequestDto, List< .orElseThrow(() -> new EntityNotFoundException("해당 리뷰를 찾을 수 없습니다.")); Product product = existingReview.getProduct(); - - // 기존 평점 제거 후 새로운 평점 반영 double newStarRate = ((product.getStarRate() * product.getReviewCount()) - existingReview.getRating() + reviewRequestDto.getRating()) / product.getReviewCount(); product.setStarRate(newStarRate); productRepository.save(product); - // 리뷰 정보 업데이트 existingReview.setRating(reviewRequestDto.getRating()); existingReview.setContent(reviewRequestDto.getContent()); List oldImageUrls = existingReview.getImageUrls(); @@ -177,120 +128,66 @@ public void updateReview(Long reviewId, ReviewRequestDto reviewRequestDto, List< existingReview.setImageUrls(newImageUrls); reviewRepository.save(existingReview); - // 기존 태그 매핑 삭제 - List existingMappings = reviewTagMappingRepository.findAllByReview(existingReview); - reviewTagMappingRepository.deleteAllByReview(existingReview); - - for (ReviewTagMapping mapping : existingMappings) { - ReviewTag reviewTag = mapping.getReviewTag(); - reviewTag.setTagCount(reviewTag.getTagCount() - 1); // 기존 태그 카운트 감소 - if (reviewTag.getTagCount() <= 0) { - reviewTagRepository.delete(reviewTag); // 태그 카운트가 0이 되면 삭제 - } else { - reviewTagRepository.save(reviewTag); - } - } - - // 새로운 태그 추가 및 매핑 저장 - reviewRequestDto.getPositiveTags().forEach(tag -> { - Optional existingTag = reviewTagRepository.findByProductIdAndTag(product.getId(), tag, null); - ReviewTag reviewTag; - if (existingTag.isPresent()) { - reviewTag = existingTag.get(); - reviewTag.setTagCount(reviewTag.getTagCount() + 1); // 기존 태그 카운트 증가 - } else { - reviewTag = ReviewTag.builder() - .productId(product.getId()) - .positiveTagEnum(tag) - .tagCount(1) - .positiveNegative(true) - .build(); - } - reviewTagRepository.save(reviewTag); - reviewTagMappingRepository.save(new ReviewTagMapping(existingReview, reviewTag)); - }); - - reviewRequestDto.getNegativeTags().forEach(tag -> { - Optional existingTag = reviewTagRepository.findByProductIdAndTag(product.getId(), null, tag); - ReviewTag reviewTag; - if (existingTag.isPresent()) { - reviewTag = existingTag.get(); - reviewTag.setTagCount(reviewTag.getTagCount() + 1); // 기존 태그 카운트 증가 - } else { - reviewTag = ReviewTag.builder() - .productId(product.getId()) - .negativeTagEnum(tag) - .tagCount(1) - .positiveNegative(false) - .build(); - } - reviewTagRepository.save(reviewTag); - reviewTagMappingRepository.save(new ReviewTagMapping(existingReview, reviewTag)); - }); - - // 새로운 topTags 계산 - List topTags = reviewTagRepository.findAll().stream() - .filter(tag -> tag.getPositiveNegative() && tag.getProductId() == product.getId()) - .sorted((t1, t2) -> t2.getTagCount().compareTo(t1.getTagCount())) - .limit(3) - .map(ReviewTag::getPositiveTagEnum) - .collect(Collectors.toList()); - - product.setTags(topTags); - productRepository.save(product); + saveReviewTags(reviewRequestDto, existingReview, product); } @Override @Transactional public void deleteReview(Long reviewId) { - // 삭제할 리뷰 조회 Review review = reviewRepository.findById(reviewId) .orElseThrow(() -> new EntityNotFoundException("해당 리뷰를 찾을 수 없습니다.")); - - // 이미지 삭제 추가 - List oldImageUrls = review.getImageUrls(); - if(!oldImageUrls.isEmpty()) { - for (String imageUrl : oldImageUrls) { - s3Util.deleteFile(imageUrl); - } - } - Product product = review.getProduct(); - // 리뷰의 평점을 상품에서 제거 double newStarRate = ((product.getStarRate() * product.getReviewCount()) - review.getRating()) / (product.getReviewCount() - 1); - product.setStarRate(product.getReviewCount() > 1 ? newStarRate : 0.0); // 리뷰가 하나일 경우 별점 0으로 설정 + product.setStarRate(product.getReviewCount() > 1 ? newStarRate : 0.0); product.setReviewCount(product.getReviewCount() - 1); productRepository.save(product); - // 태그 매핑 제거 - List mappings = reviewTagMappingRepository.findAllByReview(review); - reviewTagMappingRepository.deleteAllByReview(review); + reviewRepository.delete(review); - for (ReviewTagMapping mapping : mappings) { - ReviewTag reviewTag = mapping.getReviewTag(); - reviewTag.setTagCount(reviewTag.getTagCount() - 1); - if (reviewTag.getTagCount() <= 0) { - reviewTagRepository.delete(reviewTag); // 태그 카운트가 0이면 삭제 - } else { - reviewTagRepository.save(reviewTag); - } - } + updateTopTags(product); + } - // 리뷰 삭제 - reviewRepository.delete(review); + private void saveReviewTags(ReviewRequestDto reviewRequestDto, Review review, Product product) { + reviewRequestDto.getPositiveTags().forEach(tag -> saveTag(product, review, tag, true)); + reviewRequestDto.getNegativeTags().forEach(tag -> saveTag(product, review, tag, false)); + updateTopTags(product); + } + + private void saveTag(Product product, Review review, Enum tag, boolean isPositive) { + Optional existingTag = reviewTagRepository.findByProductIdAndTag( + product.getId(), isPositive ? (PositiveTagEnum) tag : null, isPositive ? null : (NegativeTagEnum) tag); + ReviewTag reviewTag = existingTag.orElseGet(() -> ReviewTag.builder() + .productId(product.getId()) + .positiveTagEnum(isPositive ? (PositiveTagEnum) tag : null) + .negativeTagEnum(isPositive ? null : (NegativeTagEnum) tag) + .tagCount(0) + .positiveNegative(isPositive) + .build()); + reviewTag.setTagCount(reviewTag.getTagCount() + 1); + reviewTagRepository.save(reviewTag); + reviewTagMappingRepository.save(new ReviewTagMapping(review, reviewTag)); + } - // topTags 업데이트 - List topTags = reviewTagRepository.findAll().stream() - .filter(tag -> tag.getPositiveNegative() && tag.getProductId() == product.getId() && tag.getTagCount() > 0) - .sorted((t1, t2) -> t2.getTagCount().compareTo(t1.getTagCount())) + private void updateTopTags(Product product) { + List topPositiveTags = reviewTagRepository.findAll().stream() + .filter(tag -> tag.getPositiveNegative() && tag.getProductId().equals(product.getId())) + .sorted(Comparator.comparingInt(ReviewTag::getTagCount).reversed()) .limit(3) .map(ReviewTag::getPositiveTagEnum) .collect(Collectors.toList()); + product.setPositiveTop3tags(topPositiveTags); + + List topNegativeTags = reviewTagRepository.findAll().stream() + .filter(tag -> !tag.getPositiveNegative() && tag.getProductId().equals(product.getId())) + .sorted(Comparator.comparingInt(ReviewTag::getTagCount).reversed()) + .limit(3) + .map(ReviewTag::getNegativeTagEnum) + .collect(Collectors.toList()); + product.setNegativeTop3tags(topNegativeTags); - product.setTags(topTags); productRepository.save(product); } From b4b710bc6ed0951619a1d8c0615a40b44127d513 Mon Sep 17 00:00:00 2001 From: Suhun0331 Date: Thu, 30 Jan 2025 18:42:35 +0900 Subject: [PATCH 4/6] =?UTF-8?q?Feat=20:=20=EB=82=B4=20=EC=A0=95=EB=B3=B4?= =?UTF-8?q?=20=EC=A1=B0=ED=9A=8C=20=EC=9D=B4=EB=A9=94=EC=9D=BC=20=EC=9D=BC?= =?UTF-8?q?=EC=B9=98=20=ED=99=95=EC=9D=B8=20api=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../member/controller/MemberController.java | 57 ++++++++----------- .../response/MypageInfoResponse.java | 18 ++++++ .../zeropick/member/dto/MemberEmailDto.java | 18 ++++++ .../member/service/MemberService.java | 19 +++++++ .../repository/BookmarkJpaRepository.java | 1 + .../repository/ReviewJpaRepository.java | 3 + 6 files changed, 84 insertions(+), 32 deletions(-) create mode 100644 src/main/java/kw/zeropick/member/controller/response/MypageInfoResponse.java create mode 100644 src/main/java/kw/zeropick/member/dto/MemberEmailDto.java diff --git a/src/main/java/kw/zeropick/member/controller/MemberController.java b/src/main/java/kw/zeropick/member/controller/MemberController.java index 7963a5f..f7d6df4 100644 --- a/src/main/java/kw/zeropick/member/controller/MemberController.java +++ b/src/main/java/kw/zeropick/member/controller/MemberController.java @@ -7,7 +7,9 @@ import kw.zeropick.member.controller.response.CreateMemberResponse; import kw.zeropick.member.controller.response.MemberFieldResponse; import kw.zeropick.member.controller.response.MemberInfoResponse; +import kw.zeropick.member.controller.response.MypageInfoResponse; import kw.zeropick.member.domain.Member; +import kw.zeropick.member.dto.MemberEmailDto; import kw.zeropick.member.dto.MemberFieldDto; import kw.zeropick.member.dto.MemberInfoChangeDto; import kw.zeropick.member.dto.MemberJoinDto; @@ -28,18 +30,6 @@ public class MemberController { private final MemberService memberService; - @Operation( - summary = "회원가입 요청", - description = "회원가입 요청을 받아 성공/실패 여부를 반환합니다.") - @PostMapping - public ResponseEntity saveMember(@RequestBody @Valid MemberJoinDto memberJoinDto) { - Member joinMember = memberService.join(memberJoinDto); - - return ResponseEntity - .status(HttpStatus.CREATED) - .body(new CreateMemberResponse(joinMember.getId(), "Member created successfully")); - } - @Operation( summary = "내 정보 조회", description = "마이페이지에서 내 정보들을 가져옵니다.") @@ -62,26 +52,29 @@ public ResponseEntity changeMemberInfo(@RequestBody @Valid MemberInfoCh return ResponseEntity.ok(Boolean.TRUE); } -// @Operation( -// summary = "관심분야 조회", -// description = "마이페이지에서 관심분야를 조회합니다.") -// @GetMapping("/myPage/field") -// public ResponseEntity getField() { -// Long loginUser = LoginUser.get().getId(); -// List memberField = memberService.getMemberField(loginUser); -// return ResponseEntity.ok().body(new MemberFieldResponse(memberField)); -// } - -// @Operation( -// summary = "관심분야 등록/수정", -// description = "초기/마이페이지에서 관심분야를 등록/수정합니다.") -// @PostMapping({"/field", "/myPage/field"}) -// public ResponseEntity postField(@RequestBody MemberFieldDto memberFieldDto) { -// -// Long loginUserId = LoginUser.get().getId(); -// memberService.updateMemberField(loginUserId, memberFieldDto); -// return ResponseEntity.ok(Boolean.TRUE); -// } + @Operation( + summary = "마이페이지 클릭시 정보 조회", + description = "마이페이지 클릭 시 닉네임, 찜한 제품 수, 내가 쓴 리뷰 수를 반환합니다.") + @GetMapping("/myPage") + public ResponseEntity getMypageInfo() { + Long loginUser = LoginUser.get().getId(); + MypageInfoResponse mypageInfoResponse = memberService.getMypageInfo(loginUser); + return ResponseEntity + .status(HttpStatus.OK) + .body(mypageInfoResponse); + } + + @Operation( + summary = "내정보 조회 이메일 일치 확인", + description = "내 정보를 조회를 위해 이메일 일치 여부를 확인합니다.") + @PostMapping("/checkEmail") + public ResponseEntity checkEmail(@RequestBody MemberEmailDto memberEmailDto) { + Long loginUser = LoginUser.get().getId(); + Member member = memberService.getById(loginUser); + + return ResponseEntity.ok(member.getEmail().equals(memberEmailDto.getEmail())); + } + } diff --git a/src/main/java/kw/zeropick/member/controller/response/MypageInfoResponse.java b/src/main/java/kw/zeropick/member/controller/response/MypageInfoResponse.java new file mode 100644 index 0000000..645b5d0 --- /dev/null +++ b/src/main/java/kw/zeropick/member/controller/response/MypageInfoResponse.java @@ -0,0 +1,18 @@ +package kw.zeropick.member.controller.response; + +import lombok.Builder; +import lombok.Data; + +@Data +public class MypageInfoResponse { + private String nickname; + private Long bookmarkCount; + private Long reviewCount; + + @Builder + public MypageInfoResponse(String nickname, Long bookmarkCount, Long reviewCount) { + this.nickname = nickname; + this.bookmarkCount = bookmarkCount; + this.reviewCount = reviewCount; + } +} diff --git a/src/main/java/kw/zeropick/member/dto/MemberEmailDto.java b/src/main/java/kw/zeropick/member/dto/MemberEmailDto.java new file mode 100644 index 0000000..343169a --- /dev/null +++ b/src/main/java/kw/zeropick/member/dto/MemberEmailDto.java @@ -0,0 +1,18 @@ +package kw.zeropick.member.dto; + +import jakarta.validation.constraints.Email; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +@Data +@NoArgsConstructor +public class MemberEmailDto { + @Email + private String email; + + @Builder + public MemberEmailDto(String email) { + this.email = email; + } +} \ No newline at end of file diff --git a/src/main/java/kw/zeropick/member/service/MemberService.java b/src/main/java/kw/zeropick/member/service/MemberService.java index 8711dd3..3eece19 100644 --- a/src/main/java/kw/zeropick/member/service/MemberService.java +++ b/src/main/java/kw/zeropick/member/service/MemberService.java @@ -3,6 +3,7 @@ import jakarta.persistence.EntityNotFoundException; import kw.zeropick.common.domain.exception.ResourceNotFoundException; import kw.zeropick.member.controller.response.MemberInfoResponse; +import kw.zeropick.member.controller.response.MypageInfoResponse; import kw.zeropick.member.domain.Member; import kw.zeropick.member.domain.MemberInterest; import kw.zeropick.member.domain.exception.ConfirmPasswordMismatchException; @@ -12,6 +13,8 @@ import kw.zeropick.member.dto.MemberInfoChangeDto; import kw.zeropick.member.dto.MemberJoinDto; import kw.zeropick.member.repository.MemberJpaRepository; +import kw.zeropick.product.repository.BookmarkJpaRepository; +import kw.zeropick.review.repository.ReviewJpaRepository; import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; @@ -23,6 +26,8 @@ @RequiredArgsConstructor public class MemberService { private final MemberJpaRepository memberJpaRepository; + private final BookmarkJpaRepository bookmarkRepository; + private final ReviewJpaRepository reviewRepository; public Member getById(Long memberId) { return memberJpaRepository.findById(memberId) @@ -100,4 +105,18 @@ public MemberInterest getMemberInterest(Long memberId) { return member.getInterest(); } + public MypageInfoResponse getMypageInfo(Long memberId) { + Member member = memberJpaRepository.findById(memberId) + .orElseThrow(() -> new EntityNotFoundException("해당 회원을 찾을 수 없습니다. ID: " + memberId)); + + Long bookmarkCount = bookmarkRepository.countByMember(member); + Long reviewCount = reviewRepository.countByMember(member); + + return MypageInfoResponse.builder() + .nickname(member.getName()) + .bookmarkCount(bookmarkCount) + .reviewCount(reviewCount) + .build(); + } + } diff --git a/src/main/java/kw/zeropick/product/repository/BookmarkJpaRepository.java b/src/main/java/kw/zeropick/product/repository/BookmarkJpaRepository.java index c7cfd82..6850e11 100644 --- a/src/main/java/kw/zeropick/product/repository/BookmarkJpaRepository.java +++ b/src/main/java/kw/zeropick/product/repository/BookmarkJpaRepository.java @@ -14,5 +14,6 @@ public interface BookmarkJpaRepository extends JpaRepository { Optional findByProductAndMember(Product product, Member member); Page findAllByMember(Member member, Pageable pageable); boolean existsByProductIdAndMemberId(Long productId, Long memberId); + Long countByMember(Member member); } diff --git a/src/main/java/kw/zeropick/review/repository/ReviewJpaRepository.java b/src/main/java/kw/zeropick/review/repository/ReviewJpaRepository.java index c722488..8c866a3 100644 --- a/src/main/java/kw/zeropick/review/repository/ReviewJpaRepository.java +++ b/src/main/java/kw/zeropick/review/repository/ReviewJpaRepository.java @@ -1,5 +1,6 @@ package kw.zeropick.review.repository; +import kw.zeropick.member.domain.Member; import kw.zeropick.review.domain.Review; import org.springframework.data.domain.Page; import org.springframework.data.domain.Pageable; @@ -10,4 +11,6 @@ public interface ReviewJpaRepository extends JpaRepository, ReviewQueryDslRepository { @Query("SELECT COUNT(r) FROM Review r WHERE r.product.id = :productId AND r.rating = :rating") Long countByProductIdAndRating(Long productId, Long rating); + + Long countByMember(Member member); } From dcb7a658b2a4a7103e07dfcb5c4ac2108077b17a Mon Sep 17 00:00:00 2001 From: Suhun0331 Date: Thu, 30 Jan 2025 22:24:16 +0900 Subject: [PATCH 5/6] =?UTF-8?q?Feat=20:=20=EB=A7=88=EC=9D=B4=ED=8E=98?= =?UTF-8?q?=EC=9D=B4=EC=A7=80=20=EB=82=B4=20=EC=A0=95=EB=B3=B4=20=EC=A1=B0?= =?UTF-8?q?=ED=9A=8C,=20=EC=88=98=EC=A0=95=20api=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../kw/zeropick/member/domain/QMember.java | 6 +- .../member/controller/MemberController.java | 12 +-- .../response/MemberInfoResponse.java | 18 ++--- .../kw/zeropick/member/domain/Member.java | 31 +++++--- .../member/dto/MemberInfoChangeDto.java | 27 +++---- .../kw/zeropick/member/dto/MemberJoinDto.java | 14 ++-- .../member/service/MemberService.java | 73 +++++-------------- .../review/service/ReviewServiceImpl.java | 2 +- 8 files changed, 74 insertions(+), 109 deletions(-) diff --git a/src/main/generated/kw/zeropick/member/domain/QMember.java b/src/main/generated/kw/zeropick/member/domain/QMember.java index c5d5553..6f9904b 100644 --- a/src/main/generated/kw/zeropick/member/domain/QMember.java +++ b/src/main/generated/kw/zeropick/member/domain/QMember.java @@ -23,15 +23,15 @@ public class QMember extends EntityPathBase { public final DatePath deleteDate = createDate("deleteDate", java.time.LocalDate.class); + public final BooleanPath diabetes = createBoolean("diabetes"); + public final StringPath email = createString("email"); public final NumberPath id = createNumber("id", Long.class); public final EnumPath interest = createEnum("interest", MemberInterest.class); - public final EnumPath marketingAgree = createEnum("marketingAgree", MarketingAgree.class); - - public final StringPath name = createString("name"); + public final StringPath nickname = createString("nickname"); public final StringPath password = createString("password"); diff --git a/src/main/java/kw/zeropick/member/controller/MemberController.java b/src/main/java/kw/zeropick/member/controller/MemberController.java index f7d6df4..81c0111 100644 --- a/src/main/java/kw/zeropick/member/controller/MemberController.java +++ b/src/main/java/kw/zeropick/member/controller/MemberController.java @@ -10,9 +10,7 @@ import kw.zeropick.member.controller.response.MypageInfoResponse; import kw.zeropick.member.domain.Member; import kw.zeropick.member.dto.MemberEmailDto; -import kw.zeropick.member.dto.MemberFieldDto; import kw.zeropick.member.dto.MemberInfoChangeDto; -import kw.zeropick.member.dto.MemberJoinDto; import kw.zeropick.member.service.MemberService; import lombok.RequiredArgsConstructor; import org.springframework.http.HttpStatus; @@ -44,12 +42,14 @@ public ResponseEntity getInfo() { @Operation( summary = "내 정보 수정", - description = "내 정보 수정 요청을 받아 성공/실패를 반환합니다.") + description = "내 정보 수정 요청을 받아 수정된 정보를 반환합니다.") @PutMapping("/myPage/info") - public ResponseEntity changeMemberInfo(@RequestBody @Valid MemberInfoChangeDto memberInfoChangeDto) { + public ResponseEntity changeMemberInfo(@RequestBody @Valid MemberInfoChangeDto memberInfoChangeDto) { Long loginUser = LoginUser.get().getId(); - memberService.updateMemberInfo(loginUser, memberInfoChangeDto); - return ResponseEntity.ok(Boolean.TRUE); + MemberInfoResponse updatedInfo = memberService.updateMemberInfo(loginUser, memberInfoChangeDto); + return ResponseEntity + .status(HttpStatus.OK) + .body(updatedInfo); } @Operation( diff --git a/src/main/java/kw/zeropick/member/controller/response/MemberInfoResponse.java b/src/main/java/kw/zeropick/member/controller/response/MemberInfoResponse.java index 30d3db1..d37b30e 100644 --- a/src/main/java/kw/zeropick/member/controller/response/MemberInfoResponse.java +++ b/src/main/java/kw/zeropick/member/controller/response/MemberInfoResponse.java @@ -1,6 +1,7 @@ package kw.zeropick.member.controller.response; +import kw.zeropick.member.domain.MemberInterest; import lombok.Builder; import lombok.Data; @@ -9,18 +10,15 @@ @Data public class MemberInfoResponse { private String email; - private String name; - private String phoneNumber; - private LocalDate birthDate; - - public MemberInfoResponse() { - } + private String nickname; + private MemberInterest interest; + private Boolean diabetes; @Builder - public MemberInfoResponse(String email, String name, String phoneNumber, LocalDate birthDate) { + public MemberInfoResponse(String email, String nickname, MemberInterest interest, Boolean diabetes) { this.email = email; - this.name = name; - this.phoneNumber = phoneNumber; - this.birthDate = birthDate; + this.nickname = nickname; + this.interest = interest; + this.diabetes = diabetes; } } diff --git a/src/main/java/kw/zeropick/member/domain/Member.java b/src/main/java/kw/zeropick/member/domain/Member.java index 3d0ada7..f5f0ead 100644 --- a/src/main/java/kw/zeropick/member/domain/Member.java +++ b/src/main/java/kw/zeropick/member/domain/Member.java @@ -24,7 +24,7 @@ public class Member { private String email; @NotNull - private String name; + private String nickname; @NotNull private String phoneNumber; @@ -35,12 +35,12 @@ public class Member { @NotNull private String password; + //관심 있는 제로 종류(제로 슈거 / 제로 칼로리) @Enumerated(EnumType.STRING) private MemberInterest interest; - @NotNull - @Enumerated(EnumType.STRING) - private MarketingAgree marketingAgree; + //당뇨 환자 여부 + private Boolean diabetes; @NotNull @Enumerated(EnumType.STRING) @@ -49,24 +49,31 @@ public class Member { private LocalDate deleteDate; - public Member( String email, String name, String phoneNumber, LocalDate birthDate, String password, MarketingAgree marketingAgree, State userState) { + public Member( String email, String nickname, String phoneNumber, LocalDate birthDate, String password, State userState) { this.email = email; - this.name = name; + this.nickname = nickname; this.phoneNumber = phoneNumber; this.birthDate = birthDate; this.password = password; - this.marketingAgree = marketingAgree; this.userState = userState; } -// public void changeFieldInfo(List field){ -// this.field = field; -// } - public void changeMemberInfo(String phoneNumber, LocalDate birthDate, MarketingAgree marketingAgree){ + public void changeMemberInfo(String phoneNumber, LocalDate birthDate){ this.phoneNumber = phoneNumber; this.birthDate = birthDate; - this.marketingAgree = marketingAgree; + } + + public void setNickname(String nickname){ + this.nickname = nickname; + } + + public void setInterest(MemberInterest interest){ + this.interest = interest; + } + + public void setDiabetes(Boolean diabetes){ + this.diabetes = diabetes; } diff --git a/src/main/java/kw/zeropick/member/dto/MemberInfoChangeDto.java b/src/main/java/kw/zeropick/member/dto/MemberInfoChangeDto.java index 2056dfd..c6c73bb 100644 --- a/src/main/java/kw/zeropick/member/dto/MemberInfoChangeDto.java +++ b/src/main/java/kw/zeropick/member/dto/MemberInfoChangeDto.java @@ -1,28 +1,25 @@ package kw.zeropick.member.dto; -import jakarta.validation.constraints.NotNull; -import kw.zeropick.member.domain.MarketingAgree; +import kw.zeropick.member.domain.MemberInterest; import lombok.Builder; import lombok.Data; import lombok.NoArgsConstructor; -import java.time.LocalDate; - @Data -@NoArgsConstructor public class MemberInfoChangeDto { - @NotNull - private String phoneNumber; - @NotNull - private LocalDate birthDate; - @NotNull - private MarketingAgree marketingAgree; + + private String email; + private String nickname; + private MemberInterest interest; + private Boolean diabetes; @Builder - public MemberInfoChangeDto(String phoneNumber, LocalDate birthDate, MarketingAgree marketingAgree) { - this.phoneNumber = phoneNumber; - this.birthDate = birthDate; - this.marketingAgree = marketingAgree; + public MemberInfoChangeDto(String email, String nickname, MemberInterest interest, Boolean diabetes) { + this.email = email; + this.nickname = nickname; + this.interest = interest; + this.diabetes = diabetes; } + } diff --git a/src/main/java/kw/zeropick/member/dto/MemberJoinDto.java b/src/main/java/kw/zeropick/member/dto/MemberJoinDto.java index b7aef89..1f6dc3b 100644 --- a/src/main/java/kw/zeropick/member/dto/MemberJoinDto.java +++ b/src/main/java/kw/zeropick/member/dto/MemberJoinDto.java @@ -17,7 +17,7 @@ public class MemberJoinDto { @NotNull private String email; @NotNull - private String name; + private String nickname; @NotNull private String phoneNumber; @NotNull @@ -27,8 +27,6 @@ public class MemberJoinDto { @NotNull private String passwordConfirm; @NotNull - private MarketingAgree marketingAgree; - @NotNull private State userState; // @Builder @@ -44,26 +42,24 @@ public class MemberJoinDto { // } @Builder - public MemberJoinDto(String email, String name, String phoneNumber, LocalDate birthDate, - String password, String passwordConfirm, MarketingAgree marketingAgree, State userState) { + public MemberJoinDto(String email, String nickname, String phoneNumber, LocalDate birthDate, + String password, String passwordConfirm, State userState) { this.email = email; - this.name = name; + this.nickname = nickname; this.phoneNumber = phoneNumber; this.birthDate = birthDate; this.password = password; this.passwordConfirm = passwordConfirm; - this.marketingAgree = marketingAgree; this.userState = userState; } public Member toEntity() { return Member.builder() .email(email) - .name(name) + .nickname(nickname) .phoneNumber(phoneNumber) .birthDate(birthDate) .password(password) - .marketingAgree(marketingAgree) .userState(userState) .build(); } diff --git a/src/main/java/kw/zeropick/member/service/MemberService.java b/src/main/java/kw/zeropick/member/service/MemberService.java index 3eece19..ab63cb7 100644 --- a/src/main/java/kw/zeropick/member/service/MemberService.java +++ b/src/main/java/kw/zeropick/member/service/MemberService.java @@ -34,69 +34,36 @@ public Member getById(Long memberId) { .orElseThrow(() -> new ResourceNotFoundException("Member", memberId)); } + public MemberInfoResponse getMemberInfo(Long memberId) { + Member member = memberJpaRepository.findById(memberId) + .orElseThrow(() -> new EntityNotFoundException("해당 회원을 찾을 수 없습니다. ID: " + memberId)); - //아직 인터페이스 구현 x, 추후에 구현 후 MemberService -> MemberServiceImpl로 수정 예정. - @Transactional - public Member join(MemberJoinDto memberJoinDto) { - - String passwordConfirm = memberJoinDto.getPasswordConfirm(); - if (!passwordConfirm.equals(memberJoinDto.getPassword())) { - throw new ConfirmPasswordMismatchException(); - } - - Member joinMember = memberJoinDto.toEntity(); - memberJpaRepository.save(joinMember); - return joinMember; + return MemberInfoResponse.builder() + .email(member.getEmail()) + .nickname(member.getNickname()) + .interest(member.getInterest()) + .diabetes(member.getDiabetes()) + .build(); } - /* security 의존성 추가후 변경할 join 함수 */ -// @Transactional -// public Long join(Member member) { -// member.changeMemberPassword(passwordEncoder.encode(member.getPassword())); -// memberJpaRepository.save(member); -// return member.getId(); -// } + public MemberInfoResponse updateMemberInfo(Long memberId, MemberInfoChangeDto memberInfoChangeDto) { + Member member = memberJpaRepository.findById(memberId) + .orElseThrow(() -> new EntityNotFoundException("해당 회원을 찾을 수 없습니다. ID: " + memberId)); - public MemberInfoResponse getMemberInfo(Long memberId) { - Member member = this.getById(memberId); + member.setNickname(memberInfoChangeDto.getNickname()); + member.setInterest(memberInfoChangeDto.getInterest()); + member.setDiabetes(memberInfoChangeDto.getDiabetes()); - if(member.getEmail() == null || member.getName() == null || member.getPhoneNumber() == null || member.getBirthDate() == null){ - throw new InvalidMemberDataException(); - } + memberJpaRepository.save(member); return MemberInfoResponse.builder() .email(member.getEmail()) - .name(member.getName()) - .phoneNumber(member.getPhoneNumber()) - .birthDate(member.getBirthDate()) + .nickname(member.getNickname()) + .interest(member.getInterest()) + .diabetes(member.getDiabetes()) .build(); } -// public List getMemberField(Long memberId){ -// Member member = this.getById(memberId); -// return member.getField(); -// } -// -// @Transactional -// public Member updateMemberField(Long memberId, MemberFieldDto memberFieldDto){ -// Member member = this.getById(memberId); -// member.changeFieldInfo(memberFieldDto.getField()); -// -// if(!member.getField().equals(memberFieldDto.getField())){ -// throw new FieldUpdateException(); -// } -// return member; -// } - - @Transactional - public Member updateMemberInfo(Long memberId, MemberInfoChangeDto memberInfoChangeDto){ - Member member = this.getById(memberId); - member.changeMemberInfo(memberInfoChangeDto.getPhoneNumber(), memberInfoChangeDto.getBirthDate(), memberInfoChangeDto.getMarketingAgree()); - if(member.getPhoneNumber() == null || member.getBirthDate() == null || member.getMarketingAgree() == null){ - throw new InvalidMemberDataException(); - } - return member; - } public MemberInterest getMemberInterest(Long memberId) { Member member = memberJpaRepository.findById(memberId) @@ -113,7 +80,7 @@ public MypageInfoResponse getMypageInfo(Long memberId) { Long reviewCount = reviewRepository.countByMember(member); return MypageInfoResponse.builder() - .nickname(member.getName()) + .nickname(member.getNickname()) .bookmarkCount(bookmarkCount) .reviewCount(reviewCount) .build(); diff --git a/src/main/java/kw/zeropick/review/service/ReviewServiceImpl.java b/src/main/java/kw/zeropick/review/service/ReviewServiceImpl.java index 8fef0d9..3a4668b 100644 --- a/src/main/java/kw/zeropick/review/service/ReviewServiceImpl.java +++ b/src/main/java/kw/zeropick/review/service/ReviewServiceImpl.java @@ -50,7 +50,7 @@ public Page getReviews(Long productId, PositiveTagEnum positiveT // myReview 값 설정 return reviewsByProductId.map(reviewResponse -> { - boolean isMyReview = reviewResponse.getUserName() != null && currentMember.getName().equals(reviewResponse.getUserName()); + boolean isMyReview = reviewResponse.getUserName() != null && currentMember.getNickname().equals(reviewResponse.getUserName()); reviewResponse.setMyReview(isMyReview); return reviewResponse; }); From ef55ec58061f57c6d62aa5a4e897dc335e86dd0c Mon Sep 17 00:00:00 2001 From: Suhun0331 Date: Thu, 30 Jan 2025 22:40:43 +0900 Subject: [PATCH 6/6] =?UTF-8?q?Feat=20:=20=EB=82=B4=EC=A0=95=EB=B3=B4=20?= =?UTF-8?q?=EC=A1=B0=ED=9A=8C=20=EC=9D=B8=EC=A6=9D=EC=9A=A9=20=EB=A7=88?= =?UTF-8?q?=EC=8A=A4=ED=82=B9=20=EC=9D=B4=EB=A9=94=EC=9D=BC=20=EC=A1=B0?= =?UTF-8?q?=ED=9A=8C=20api=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../member/controller/MemberController.java | 19 +++++++++--- .../response/MaskingEmailResponse.java | 14 +++++++++ .../member/service/MemberService.java | 29 +++++++++++++++++++ 3 files changed, 58 insertions(+), 4 deletions(-) create mode 100644 src/main/java/kw/zeropick/member/controller/response/MaskingEmailResponse.java diff --git a/src/main/java/kw/zeropick/member/controller/MemberController.java b/src/main/java/kw/zeropick/member/controller/MemberController.java index 81c0111..5a0cd52 100644 --- a/src/main/java/kw/zeropick/member/controller/MemberController.java +++ b/src/main/java/kw/zeropick/member/controller/MemberController.java @@ -4,10 +4,7 @@ import io.swagger.v3.oas.annotations.tags.Tag; import jakarta.validation.Valid; import kw.zeropick.common.LoginUser; -import kw.zeropick.member.controller.response.CreateMemberResponse; -import kw.zeropick.member.controller.response.MemberFieldResponse; -import kw.zeropick.member.controller.response.MemberInfoResponse; -import kw.zeropick.member.controller.response.MypageInfoResponse; +import kw.zeropick.member.controller.response.*; import kw.zeropick.member.domain.Member; import kw.zeropick.member.dto.MemberEmailDto; import kw.zeropick.member.dto.MemberInfoChangeDto; @@ -64,6 +61,20 @@ public ResponseEntity getMypageInfo() { .body(mypageInfoResponse); } + @Operation( + summary = "마스킹 이메일 조회", + description = "내 정보 조회를 위한 마스킹 이메일을 불러옵니다.") + @GetMapping("/getEmail") + public ResponseEntity getEmail() { + + Member member = memberService.getById(LoginUser.get().getId()); + MaskingEmailResponse response = memberService.getMemberEmail(member); + + return ResponseEntity + .status(HttpStatus.OK) + .body(response); + } + @Operation( summary = "내정보 조회 이메일 일치 확인", description = "내 정보를 조회를 위해 이메일 일치 여부를 확인합니다.") diff --git a/src/main/java/kw/zeropick/member/controller/response/MaskingEmailResponse.java b/src/main/java/kw/zeropick/member/controller/response/MaskingEmailResponse.java new file mode 100644 index 0000000..11d3181 --- /dev/null +++ b/src/main/java/kw/zeropick/member/controller/response/MaskingEmailResponse.java @@ -0,0 +1,14 @@ +package kw.zeropick.member.controller.response; + +import lombok.Builder; +import lombok.Data; + +@Data +public class MaskingEmailResponse { + private String maskEmail; + + @Builder + public MaskingEmailResponse(String maskEmail) { + this.maskEmail = maskEmail; + } +} diff --git a/src/main/java/kw/zeropick/member/service/MemberService.java b/src/main/java/kw/zeropick/member/service/MemberService.java index ab63cb7..4ff0e6c 100644 --- a/src/main/java/kw/zeropick/member/service/MemberService.java +++ b/src/main/java/kw/zeropick/member/service/MemberService.java @@ -2,6 +2,7 @@ import jakarta.persistence.EntityNotFoundException; import kw.zeropick.common.domain.exception.ResourceNotFoundException; +import kw.zeropick.member.controller.response.MaskingEmailResponse; import kw.zeropick.member.controller.response.MemberInfoResponse; import kw.zeropick.member.controller.response.MypageInfoResponse; import kw.zeropick.member.domain.Member; @@ -86,4 +87,32 @@ public MypageInfoResponse getMypageInfo(Long memberId) { .build(); } + + public MaskingEmailResponse getMemberEmail(Member member) { + if(member.getEmail() == null){ + throw new InvalidMemberDataException(); + } + String maskEmail = maskEmail(member.getEmail()); + + return MaskingEmailResponse.builder() + .maskEmail(maskEmail) + .build(); + } + + private static String maskEmail(String email) { + String[] parts = email.split("@"); + if (parts.length != 2) { + throw new IllegalArgumentException("유효하지 않은 이메일 형식입니다."); + } + + String localPart = parts[0]; + String domainPart = parts[1]; + + String maskedLocalPart = localPart.length() > 2 + ? localPart.substring(0, 2) + "*".repeat(localPart.length() - 2) + : localPart; + + return maskedLocalPart + "@" + domainPart; + } + }