diff --git a/src/main/java/com/pyonsnalcolor/config/SecurityConfig.java b/src/main/java/com/pyonsnalcolor/config/SecurityConfig.java deleted file mode 100644 index 1055ea2..0000000 --- a/src/main/java/com/pyonsnalcolor/config/SecurityConfig.java +++ /dev/null @@ -1,80 +0,0 @@ -package com.pyonsnalcolor.config; - -import com.pyonsnalcolor.member.security.AuthUserDetailsService; -import com.pyonsnalcolor.member.security.JwtAuthenticationFilter; -import com.pyonsnalcolor.handler.JwtAccessDeniedHandler; -import com.pyonsnalcolor.handler.JwtAuthenticationEntryPoint; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.boot.web.client.RestTemplateBuilder; -import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.Configuration; -import org.springframework.security.config.annotation.web.builders.HttpSecurity; -import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; -import org.springframework.security.config.annotation.web.configuration.WebSecurityCustomizer; -import org.springframework.security.config.http.SessionCreationPolicy; -import org.springframework.security.core.userdetails.UserDetailsService; -import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; -import org.springframework.security.crypto.password.PasswordEncoder; -import org.springframework.security.web.SecurityFilterChain; -import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter; -import org.springframework.web.client.RestTemplate; - -@EnableWebSecurity -@Configuration -public class SecurityConfig { - - @Autowired - JwtAuthenticationFilter jwtAuthenticationFilter; - - @Autowired - JwtAuthenticationEntryPoint jwtAuthenticationEntryPoint; - - @Autowired - JwtAccessDeniedHandler jwtAccessDeniedHandler; - - @Bean - public WebSecurityCustomizer webSecurityCustomizer() { - return (web) -> web.ignoring() - .antMatchers( "/resources/**", - "/v3/api-docs/**", - "/swagger-ui/**", - "/health-check" - ); - } - - @Bean - public SecurityFilterChain filterChain(HttpSecurity http) throws Exception { - http - .httpBasic().disable() - .csrf().disable() - .sessionManagement() - .sessionCreationPolicy(SessionCreationPolicy.STATELESS) - .and() - .authorizeRequests() - .antMatchers("/auth/**", "/promotions/**", "/fcm/**", "/manage/**").permitAll() - .antMatchers("/member/**").hasRole("USER") - .anyRequest().authenticated() - .and() - .exceptionHandling((exceptions) -> exceptions - .authenticationEntryPoint(jwtAuthenticationEntryPoint) - .accessDeniedHandler(jwtAccessDeniedHandler)) - .addFilterBefore(jwtAuthenticationFilter, UsernamePasswordAuthenticationFilter.class); - - return http.build(); - } - - @Bean - public PasswordEncoder passwordEncoder() { - return new BCryptPasswordEncoder(); - } - - @Bean - public UserDetailsService userDetailsService() { - return new AuthUserDetailsService(); - } - - @Bean - 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 afa4465..7dc88b2 100644 --- a/src/main/java/com/pyonsnalcolor/product/controller/EventProductController.java +++ b/src/main/java/com/pyonsnalcolor/product/controller/EventProductController.java @@ -68,4 +68,22 @@ public ResponseEntity getEventProduct( ProductResponseDto result = memberService.updateProductIfFavorite(product, ProductType.EVENT, memberId); return new ResponseEntity(result, HttpStatus.OK); } + + @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); + + return ResponseEntity.ok().build(); + } + + @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); + + 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 3e055fd..10ef93f 100644 --- a/src/main/java/com/pyonsnalcolor/product/controller/PbProductController.java +++ b/src/main/java/com/pyonsnalcolor/product/controller/PbProductController.java @@ -12,7 +12,9 @@ import io.swagger.v3.oas.annotations.Parameter; import io.swagger.v3.oas.annotations.tags.Tag; import lombok.RequiredArgsConstructor; -import org.springframework.data.domain.*; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.PageRequest; +import org.springframework.data.domain.Pageable; import org.springframework.http.HttpStatus; import org.springframework.http.MediaType; import org.springframework.http.ResponseEntity; @@ -66,4 +68,22 @@ public ResponseEntity getPbProduct( ProductResponseDto result = memberService.updateProductIfFavorite(product, ProductType.PB, memberId); return new ResponseEntity(result, HttpStatus.OK); } -} \ No newline at end of file + + @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); + + return ResponseEntity.ok().build(); + } + + @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); + + return ResponseEntity.ok().build(); + } +} diff --git a/src/main/java/com/pyonsnalcolor/product/controller/ProductController.java b/src/main/java/com/pyonsnalcolor/product/controller/ProductController.java index 3301e11..f42d33a 100644 --- a/src/main/java/com/pyonsnalcolor/product/controller/ProductController.java +++ b/src/main/java/com/pyonsnalcolor/product/controller/ProductController.java @@ -10,9 +10,7 @@ import org.springframework.data.domain.Page; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; -import org.springframework.web.bind.annotation.GetMapping; -import org.springframework.web.bind.annotation.RequestParam; -import org.springframework.web.bind.annotation.RestController; +import org.springframework.web.bind.annotation.*; import java.util.List; import java.util.Map; @@ -51,4 +49,4 @@ public ResponseEntity getCurationProducts() { CurationProductsResponseDto result = searchProductService.getCurationProducts(); return new ResponseEntity(result, HttpStatus.OK); } -} \ No newline at end of file +} diff --git a/src/main/java/com/pyonsnalcolor/product/dto/ReviewDto.java b/src/main/java/com/pyonsnalcolor/product/dto/ReviewDto.java index 32bd662..9e3d2ee 100644 --- a/src/main/java/com/pyonsnalcolor/product/dto/ReviewDto.java +++ b/src/main/java/com/pyonsnalcolor/product/dto/ReviewDto.java @@ -32,4 +32,6 @@ public class ReviewDto { @JsonSerialize(using = LocalDateTimeSerializer.class) @JsonDeserialize(using = LocalDateTimeDeserializer.class) private LocalDateTime updatedTime; + private Long likeCount; + private Long hateCount; } diff --git a/src/main/java/com/pyonsnalcolor/product/entity/Review.java b/src/main/java/com/pyonsnalcolor/product/entity/Review.java index a556d48..350b4f3 100644 --- a/src/main/java/com/pyonsnalcolor/product/entity/Review.java +++ b/src/main/java/com/pyonsnalcolor/product/entity/Review.java @@ -2,16 +2,17 @@ import com.pyonsnalcolor.product.dto.ReviewDto; import com.pyonsnalcolor.product.enumtype.Like; -import lombok.AllArgsConstructor; -import lombok.Builder; -import lombok.Getter; -import lombok.NoArgsConstructor; +import lombok.*; + +import java.time.LocalDateTime; @Getter @Builder +@Setter @NoArgsConstructor @AllArgsConstructor -public class Review extends BaseTimeEntity { +public class Review { + private String reviewId; private Like taste; //맛 private Like quality; //퀄리티 private Like valueForMoney; //가성비 @@ -20,9 +21,27 @@ public class Review extends BaseTimeEntity { private String image; //이미지 private Long writerId; // 작성자 id <- 이후 기능 추가 고려 private String writerName; + private LocalDateTime createdTime; + private LocalDateTime updatedTime; + private Long likeCount; + private Long hateCount; public ReviewDto convertToDto() { return new ReviewDto(taste, quality, valueForMoney, score, contents, image, writerId, writerName, - getCreatedDate(), getModifiedDate()); + createdTime, updatedTime, likeCount, hateCount); + } + + public void likeReview() { + if(this.likeCount == null) { + this.likeCount = 0L; + } + this.likeCount += 1; + } + + public void hateReview() { + if(this.hateCount == null) { + this.hateCount = 0L; + } + 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 2cc26c8..ada0714 100644 --- a/src/main/java/com/pyonsnalcolor/product/service/ProductService.java +++ b/src/main/java/com/pyonsnalcolor/product/service/ProductService.java @@ -6,11 +6,11 @@ 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.enumtype.*; import com.pyonsnalcolor.product.repository.BasicProductRepository; import com.pyonsnalcolor.product.repository.ImageRepository; import lombok.RequiredArgsConstructor; -import lombok.extern.slf4j.Slf4j; import org.springframework.data.domain.Page; import org.springframework.data.domain.Pageable; import org.springframework.data.domain.Slice; @@ -22,6 +22,7 @@ import org.springframework.transaction.annotation.Transactional; import org.springframework.web.multipart.MultipartFile; +import java.time.LocalDateTime; import java.util.List; import java.util.NoSuchElementException; @@ -83,15 +84,52 @@ private Criteria createFilterCriteria(List recommends, List return criteria; } + //리뷰 좋아요 + public void likeReview(String productId, String reviewId) throws Throwable { + BaseProduct baseProduct = (BaseProduct) basicProductRepository + .findById(productId) + .orElseThrow(NoSuchElementException::new); + + Review review = baseProduct.getReviews().stream().filter( + r -> r.getReviewId().equals(reviewId) + ).findFirst() + .orElseThrow(NoSuchElementException::new); + + review.likeReview(); + + basicProductRepository.save(baseProduct); + } + + //리뷰 싫어요 + public void hateReview(String productId, String reviewId) throws Throwable { + BaseProduct baseProduct = (BaseProduct) basicProductRepository + .findById(productId) + .orElseThrow(NoSuchElementException::new); + + Review review = baseProduct.getReviews().stream().filter( + r -> r.getReviewId().equals(reviewId) + ).findFirst() + .orElseThrow(NoSuchElementException::new); + + review.hateReview(); + + basicProductRepository.save(baseProduct); + } + //리뷰 등록 public void registerReview(MultipartFile image, ReviewDto reviewDto, String productId) throws Throwable { BaseProduct baseProduct = (BaseProduct) basicProductRepository .findById(productId) .orElseThrow(NoSuchElementException::new); - String filePath = imageRepository.uploadImage(image); + String filePath = "None"; + + if (image != null) { + filePath = imageRepository.uploadImage(image); + } Review review = new Review().builder() + .reviewId(UUIDGenerator.generateId()) .contents(reviewDto.getContents()) .image(filePath) .quality(reviewDto.getQuality()) @@ -100,6 +138,10 @@ public void registerReview(MultipartFile image, ReviewDto reviewDto, String prod .valueForMoney(reviewDto.getValueForMoney()) .writerId(reviewDto.getWriterId()) .writerName(reviewDto.getWriterName()) + .updatedTime(LocalDateTime.now()) + .createdTime(LocalDateTime.now()) + .hateCount(0L) + .likeCount(0L) .build(); baseProduct.addReview(review); @@ -122,4 +164,4 @@ public void validateProductTypeOfProduct(String id) { throw new PyonsnalcolorProductException(INVALID_PRODUCT_TYPE); } } -} \ No newline at end of file +}