diff --git a/src/main/java/com/pyonsnalcolor/config/SecurityConfig.java b/src/main/java/com/pyonsnalcolor/config/SecurityConfig.java index 418180d..61fcd00 100644 --- a/src/main/java/com/pyonsnalcolor/config/SecurityConfig.java +++ b/src/main/java/com/pyonsnalcolor/config/SecurityConfig.java @@ -77,4 +77,4 @@ public UserDetailsService userDetailsService() { public RestTemplate restTemplate(RestTemplateBuilder restTemplateBuilder) { return restTemplateBuilder.build(); } -} \ No newline at end of file +} diff --git a/src/main/java/com/pyonsnalcolor/product/controller/EventProductController.java b/src/main/java/com/pyonsnalcolor/product/controller/EventProductController.java index 7dc88b2..81d5a61 100644 --- a/src/main/java/com/pyonsnalcolor/product/controller/EventProductController.java +++ b/src/main/java/com/pyonsnalcolor/product/controller/EventProductController.java @@ -5,7 +5,7 @@ import com.pyonsnalcolor.product.dto.EventProductResponseDto; import com.pyonsnalcolor.product.dto.ProductFilterRequestDto; import com.pyonsnalcolor.product.dto.ProductResponseDto; -import com.pyonsnalcolor.product.dto.ReviewDto; +import com.pyonsnalcolor.product.dto.ReviewRequestDto; import com.pyonsnalcolor.product.enumtype.ProductType; import com.pyonsnalcolor.product.service.EventProductService; import io.swagger.v3.oas.annotations.Operation; @@ -51,7 +51,7 @@ public ResponseEntity> getEventProductsByFilter( public ResponseEntity registerReview( @PathVariable String id, @RequestPart(value = "imageFile", required = false) MultipartFile imageFile, - @RequestPart(value = "reviewDto") ReviewDto reviewDto + @RequestPart(value = "reviewDto") ReviewRequestDto reviewDto ) throws Throwable { eventProductService.registerReview(imageFile, reviewDto, id); @@ -72,8 +72,9 @@ public ResponseEntity getEventProduct( @Operation(summary = "event 상품 리뷰 좋아요", description = "id에 해당하는 event 상품의 리뷰 좋아요 카운트 증가.") @PutMapping("/products/event-products/{productId}/reviews/{reviewId}/like") public ResponseEntity likeReview(@PathVariable("productId") String productId, - @PathVariable("reviewId") String reviewId) throws Throwable { - eventProductService.likeReview(productId, reviewId); + @PathVariable("reviewId") String reviewId, + @RequestParam("writerId") Long writerId) throws Throwable { + eventProductService.likeReview(productId, reviewId, writerId); return ResponseEntity.ok().build(); } @@ -81,8 +82,9 @@ public ResponseEntity likeReview(@PathVariable("productId") String product @Operation(summary = "event 상품 리뷰 싫어요", description = "id에 해당하는 event 상품의 리뷰 싫어요 카운트 증가.") @PutMapping("/products/event-products/{productId}/reviews/{reviewId}/hate") public ResponseEntity hateReview(@PathVariable("productId") String productId, - @PathVariable("reviewId") String reviewId) throws Throwable { - eventProductService.hateReview(productId, reviewId); + @PathVariable("reviewId") String reviewId, + @RequestParam("writerId") Long writerId) throws Throwable { + eventProductService.hateReview(productId, reviewId, writerId); return ResponseEntity.ok().build(); } diff --git a/src/main/java/com/pyonsnalcolor/product/controller/PbProductController.java b/src/main/java/com/pyonsnalcolor/product/controller/PbProductController.java index 10ef93f..bde1dfe 100644 --- a/src/main/java/com/pyonsnalcolor/product/controller/PbProductController.java +++ b/src/main/java/com/pyonsnalcolor/product/controller/PbProductController.java @@ -5,7 +5,7 @@ import com.pyonsnalcolor.product.dto.PbProductResponseDto; import com.pyonsnalcolor.product.dto.ProductFilterRequestDto; import com.pyonsnalcolor.product.dto.ProductResponseDto; -import com.pyonsnalcolor.product.dto.ReviewDto; +import com.pyonsnalcolor.product.dto.ReviewRequestDto; import com.pyonsnalcolor.product.enumtype.ProductType; import com.pyonsnalcolor.product.service.PbProductService; import io.swagger.v3.oas.annotations.Operation; @@ -51,7 +51,7 @@ public ResponseEntity> getPbProductsByFilter( public ResponseEntity registerReview( @PathVariable String id, @RequestPart(value = "imageFile", required = false) MultipartFile imageFile, - @RequestPart(value = "reviewDto") ReviewDto reviewDto + @RequestPart(value = "reviewDto") ReviewRequestDto reviewDto ) throws Throwable { pbProductService.registerReview(imageFile, reviewDto, id); @@ -72,8 +72,9 @@ public ResponseEntity getPbProduct( @Operation(summary = "PB 상품 리뷰 좋아요", description = "id에 해당하는 PB 상품의 리뷰 좋아요 카운트 증가.") @PutMapping("/products/pb-products/{productId}/reviews/{reviewId}/like") public ResponseEntity likeReview(@PathVariable("productId") String productId, - @PathVariable("reviewId") String reviewId) throws Throwable { - pbProductService.likeReview(productId, reviewId); + @PathVariable("reviewId") String reviewId, + @RequestParam("writerId") Long writerId) throws Throwable { + pbProductService.likeReview(productId, reviewId, writerId); return ResponseEntity.ok().build(); } @@ -81,8 +82,9 @@ public ResponseEntity likeReview(@PathVariable("productId") String product @Operation(summary = "PB 상품 리뷰 싫어요", description = "id에 해당하는 PB 상품의 리뷰 싫어요 카운트 증가.") @PutMapping("/products/pb-products/{productId}/reviews/{reviewId}/hate") public ResponseEntity hateReview(@PathVariable("productId") String productId, - @PathVariable("reviewId") String reviewId) throws Throwable { - pbProductService.hateReview(productId, reviewId); + @PathVariable("reviewId") String reviewId, + @RequestParam("writerId") Long writerId) throws Throwable { + pbProductService.hateReview(productId, reviewId, writerId); return ResponseEntity.ok().build(); } diff --git a/src/main/java/com/pyonsnalcolor/product/dto/ReviewDto.java b/src/main/java/com/pyonsnalcolor/product/dto/ReviewDto.java index 9e3d2ee..c595efb 100644 --- a/src/main/java/com/pyonsnalcolor/product/dto/ReviewDto.java +++ b/src/main/java/com/pyonsnalcolor/product/dto/ReviewDto.java @@ -5,6 +5,8 @@ import com.fasterxml.jackson.databind.annotation.JsonSerialize; import com.fasterxml.jackson.datatype.jsr310.deser.LocalDateTimeDeserializer; import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateTimeSerializer; +import com.pyonsnalcolor.product.entity.HateCount; +import com.pyonsnalcolor.product.entity.LikeCount; import com.pyonsnalcolor.product.enumtype.Like; import lombok.AllArgsConstructor; import lombok.Data; @@ -16,6 +18,7 @@ @AllArgsConstructor @NoArgsConstructor public class ReviewDto { + private String reviewId; private Like taste; //맛 private Like quality; //퀄리티 private Like valueForMoney; //가성비 @@ -32,6 +35,6 @@ public class ReviewDto { @JsonSerialize(using = LocalDateTimeSerializer.class) @JsonDeserialize(using = LocalDateTimeDeserializer.class) private LocalDateTime updatedTime; - private Long likeCount; - private Long hateCount; + private LikeCount likeCount; + private HateCount hateCount; } diff --git a/src/main/java/com/pyonsnalcolor/product/dto/ReviewRequestDto.java b/src/main/java/com/pyonsnalcolor/product/dto/ReviewRequestDto.java new file mode 100644 index 0000000..b8288c8 --- /dev/null +++ b/src/main/java/com/pyonsnalcolor/product/dto/ReviewRequestDto.java @@ -0,0 +1,20 @@ +package com.pyonsnalcolor.product.dto; + +import com.pyonsnalcolor.product.enumtype.Like; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +@Data +@AllArgsConstructor +@NoArgsConstructor +public class ReviewRequestDto { + private Like taste; //맛 + private Like quality; //퀄리티 + private Like valueForMoney; //가성비 + private float score; //별점 + private String contents; //내용 + + private Long writerId; //작성자 <- 이후 기능 확장 고려한 필드 + private String writerName; // 닉네임 +} diff --git a/src/main/java/com/pyonsnalcolor/product/entity/HateCount.java b/src/main/java/com/pyonsnalcolor/product/entity/HateCount.java new file mode 100644 index 0000000..76920a7 --- /dev/null +++ b/src/main/java/com/pyonsnalcolor/product/entity/HateCount.java @@ -0,0 +1,16 @@ +package com.pyonsnalcolor.product.entity; + +import lombok.Data; + +import java.util.ArrayList; +import java.util.List; + +@Data +public class HateCount { + private List writerIds = new ArrayList<>(); + private Long hateCount = 0L; + + public void addWriter(Long writerId) { + this.writerIds.add(writerId); + } +} diff --git a/src/main/java/com/pyonsnalcolor/product/entity/LikeCount.java b/src/main/java/com/pyonsnalcolor/product/entity/LikeCount.java new file mode 100644 index 0000000..986ba9c --- /dev/null +++ b/src/main/java/com/pyonsnalcolor/product/entity/LikeCount.java @@ -0,0 +1,16 @@ +package com.pyonsnalcolor.product.entity; + +import lombok.Data; + +import java.util.ArrayList; +import java.util.List; + +@Data +public class LikeCount { + private List writerIds = new ArrayList<>(); + private Long likeCount = 0L; + + public void addWriter(Long writerId) { + this.writerIds.add(writerId); + } +} diff --git a/src/main/java/com/pyonsnalcolor/product/entity/Review.java b/src/main/java/com/pyonsnalcolor/product/entity/Review.java index 350b4f3..454474d 100644 --- a/src/main/java/com/pyonsnalcolor/product/entity/Review.java +++ b/src/main/java/com/pyonsnalcolor/product/entity/Review.java @@ -23,25 +23,43 @@ public class Review { private String writerName; private LocalDateTime createdTime; private LocalDateTime updatedTime; - private Long likeCount; - private Long hateCount; + private LikeCount likeCount; + private HateCount hateCount; public ReviewDto convertToDto() { - return new ReviewDto(taste, quality, valueForMoney, score, contents, image, writerId, writerName, + return new ReviewDto(reviewId, taste, quality, valueForMoney, score, contents, image, writerId, writerName, createdTime, updatedTime, likeCount, hateCount); } - public void likeReview() { - if(this.likeCount == null) { - this.likeCount = 0L; + public void likeReview(Long writerId) { + if (this.likeCount == null) { + this.likeCount = new LikeCount(); + } + if (this.likeCount.getWriterIds().contains(writerId)) { + return; + } + this.likeCount.addWriter(writerId); + this.likeCount.setLikeCount(this.likeCount.getLikeCount() + 1); + + if (this.hateCount != null && this.hateCount.getWriterIds().contains(writerId)) { + this.hateCount.setHateCount(this.hateCount.getHateCount() - 1); + this.hateCount.getWriterIds().remove(writerId); } - this.likeCount += 1; } - public void hateReview() { - if(this.hateCount == null) { - this.hateCount = 0L; + public void hateReview(Long writerId) { + if (this.hateCount == null) { + this.hateCount = new HateCount(); + } + if (this.hateCount.getWriterIds().contains(writerId)) { + return; + } + this.hateCount.addWriter(writerId); + this.hateCount.setHateCount(this.hateCount.getHateCount() + 1); + + if (this.likeCount != null && this.likeCount.getWriterIds().contains(writerId)) { + this.likeCount.setLikeCount(this.likeCount.getLikeCount() - 1); + this.likeCount.getWriterIds().remove(writerId); } - this.hateCount += 1; } } diff --git a/src/main/java/com/pyonsnalcolor/product/service/ProductService.java b/src/main/java/com/pyonsnalcolor/product/service/ProductService.java index ada0714..ed5150f 100644 --- a/src/main/java/com/pyonsnalcolor/product/service/ProductService.java +++ b/src/main/java/com/pyonsnalcolor/product/service/ProductService.java @@ -4,9 +4,8 @@ import com.pyonsnalcolor.product.dto.ProductFilterRequestDto; import com.pyonsnalcolor.product.dto.ProductResponseDto; import com.pyonsnalcolor.product.dto.ReviewDto; -import com.pyonsnalcolor.product.entity.BaseProduct; -import com.pyonsnalcolor.product.entity.Review; -import com.pyonsnalcolor.product.entity.UUIDGenerator; +import com.pyonsnalcolor.product.dto.ReviewRequestDto; +import com.pyonsnalcolor.product.entity.*; import com.pyonsnalcolor.product.enumtype.*; import com.pyonsnalcolor.product.repository.BasicProductRepository; import com.pyonsnalcolor.product.repository.ImageRepository; @@ -85,7 +84,8 @@ private Criteria createFilterCriteria(List recommends, List } //리뷰 좋아요 - public void likeReview(String productId, String reviewId) throws Throwable { + @Transactional + public void likeReview(String productId, String reviewId, Long writerId) throws Throwable { BaseProduct baseProduct = (BaseProduct) basicProductRepository .findById(productId) .orElseThrow(NoSuchElementException::new); @@ -95,13 +95,14 @@ public void likeReview(String productId, String reviewId) throws Throwable { ).findFirst() .orElseThrow(NoSuchElementException::new); - review.likeReview(); + review.likeReview(writerId); basicProductRepository.save(baseProduct); } //리뷰 싫어요 - public void hateReview(String productId, String reviewId) throws Throwable { + @Transactional + public void hateReview(String productId, String reviewId, Long writerId) throws Throwable { BaseProduct baseProduct = (BaseProduct) basicProductRepository .findById(productId) .orElseThrow(NoSuchElementException::new); @@ -111,13 +112,13 @@ public void hateReview(String productId, String reviewId) throws Throwable { ).findFirst() .orElseThrow(NoSuchElementException::new); - review.hateReview(); + review.hateReview(writerId); basicProductRepository.save(baseProduct); } //리뷰 등록 - public void registerReview(MultipartFile image, ReviewDto reviewDto, String productId) throws Throwable { + public void registerReview(MultipartFile image, ReviewRequestDto reviewDto, String productId) throws Throwable { BaseProduct baseProduct = (BaseProduct) basicProductRepository .findById(productId) .orElseThrow(NoSuchElementException::new); @@ -140,8 +141,8 @@ public void registerReview(MultipartFile image, ReviewDto reviewDto, String prod .writerName(reviewDto.getWriterName()) .updatedTime(LocalDateTime.now()) .createdTime(LocalDateTime.now()) - .hateCount(0L) - .likeCount(0L) + .hateCount(new HateCount()) + .likeCount(new LikeCount()) .build(); baseProduct.addReview(review);