Skip to content

Commit

Permalink
Merge pull request #95 from kakao-tech-campus-2nd-step3/develop
Browse files Browse the repository at this point in the history
Develop 정기 병합
  • Loading branch information
yooonwodyd authored Nov 8, 2024
2 parents eff003f + 73be0c2 commit ccc701b
Show file tree
Hide file tree
Showing 27 changed files with 1,654 additions and 186 deletions.
2 changes: 2 additions & 0 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,8 @@ dependencies {
annotationProcessor "jakarta.annotation:jakarta.annotation-api"
annotationProcessor "jakarta.persistence:jakarta.persistence-api"

implementation 'org.springframework.boot:spring-boot-starter-data-redis'

// Spring docs
implementation 'org.springdoc:springdoc-openapi-starter-webmvc-ui:2.6.0'

Expand Down
4 changes: 2 additions & 2 deletions src/main/java/com/helpmeCookies/Step3Application.java
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,10 @@

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.context.properties.ConfigurationPropertiesScan;
import org.springframework.data.jpa.repository.config.EnableJpaAuditing;
import org.springframework.scheduling.annotation.EnableScheduling;

@SpringBootApplication
@EnableScheduling
public class Step3Application {

public static void main(String[] args) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,8 @@
@Getter
public enum SuccessCode {
OK(200, "OK"),
CREATED_SUCCESS(201, "저장에 성공했습니다");
CREATED_SUCCESS(201, "저장에 성공했습니다"),
NO_CONTENT(204, "삭제에 성공했습니다.");

private final int code;
private final String message;
Expand Down
40 changes: 40 additions & 0 deletions src/main/java/com/helpmeCookies/global/config/RedisConfig.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
package com.helpmeCookies.global.config;

import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.repository.configuration.EnableRedisRepositories;
import org.springframework.data.redis.serializer.StringRedisSerializer;

@Configuration
@EnableRedisRepositories
public class RedisConfig {

@Value("${spring.redis.host}")
private String host;

@Value("${spring.redis.port}")
private int port;

@Bean
public RedisConnectionFactory redisConnectionFactory() {
return new LettuceConnectionFactory(host, port);
}

@Bean
public RedisTemplate<?, ?> redisTemplate() {
RedisTemplate<byte[], byte[]> redisTemplate = new RedisTemplate<>();
redisTemplate.setKeySerializer(new StringRedisSerializer());
redisTemplate.setValueSerializer(new StringRedisSerializer());
redisTemplate.setHashKeySerializer(new StringRedisSerializer());
redisTemplate.setHashValueSerializer(new StringRedisSerializer());
redisTemplate.setConnectionFactory(redisConnectionFactory());
return redisTemplate;
}



}
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,8 @@
public class GlobalExceptionHandler {

@ExceptionHandler(ResourceNotFoundException.class)
public ResponseEntity<ApiResponse<Void>> handleResourceNotFoundException() {
return ResponseEntity.badRequest().body(ApiResponse.error(HttpStatus.BAD_REQUEST,"해당 리소스를 찾을 수 없습니다."));
public ResponseEntity<ApiResponse<Void>> handleResourceNotFoundException(ResourceNotFoundException e) {
return ResponseEntity.badRequest().body(ApiResponse.error(HttpStatus.BAD_REQUEST, e.getMessage()));
}

@ExceptionHandler(DuplicateRequestException.class)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -75,8 +75,7 @@ public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
"/v1/**",
"swagger-ui/**",
"/test/signup",
"/v1/artist",
"/v1/artists"
"/v1/artists/**"
).permitAll()
.anyRequest().authenticated()
).exceptionHandling((exception) -> exception
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

import com.helpmeCookies.global.ApiResponse.ApiResponse;
import com.helpmeCookies.global.ApiResponse.SuccessCode;
import com.helpmeCookies.global.jwt.JwtUser;
import com.helpmeCookies.product.dto.ImageUpload;
import static com.helpmeCookies.product.util.SortUtil.convertProductSort;

Expand All @@ -12,12 +13,18 @@
import com.helpmeCookies.product.dto.ProductResponse;
import com.helpmeCookies.product.entity.Product;
import com.helpmeCookies.product.service.ProductImageService;
import com.helpmeCookies.product.service.ProductLikeService;
import com.helpmeCookies.product.service.ProductService;
import com.helpmeCookies.product.util.ProductSort;
import com.helpmeCookies.review.dto.ReviewResponse;
import com.helpmeCookies.review.service.ReviewService;
import lombok.RequiredArgsConstructor;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.data.web.PageableDefault;
import org.springframework.http.ResponseEntity;
import org.springframework.security.core.annotation.AuthenticationPrincipal;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;

Expand All @@ -30,6 +37,8 @@ public class ProductController implements ProductApiDocs {

private final ProductService productService;
private final ProductImageService productImageService;
private final ReviewService reviewService;
private final ProductLikeService productLikeService;

@PostMapping("/successTest")
public ResponseEntity<ApiResponse<Void>> saveTest() {
Expand Down Expand Up @@ -98,4 +107,28 @@ public ResponseEntity<ProductPage.Paging> getProductsWithRandomPaging(
Pageable pageable = PageRequest.of(0, size);
return ResponseEntity.ok(productService.getProductsWithRandomPaging(pageable));
}

@GetMapping("/{productId}/reviews")
public ResponseEntity<ApiResponse<Page<ReviewResponse>>> getAllReviewsByProduct(
@PathVariable("productId") Long productId,
@PageableDefault(size = 7) Pageable pageable) {
return ResponseEntity.ok(ApiResponse.success(SuccessCode.OK,reviewService.getAllReviewByProduct(productId,pageable)));
}

@PostMapping("/{productId}/likes")
public ResponseEntity<ApiResponse<Void>> postProductLike(
@PathVariable("productId") Long productId,
@AuthenticationPrincipal JwtUser jwtUser) {
productLikeService.productLike(jwtUser.getId(), productId);
return ResponseEntity.ok(ApiResponse.success(SuccessCode.OK));
}

@DeleteMapping("/{productId}/likes")
public ResponseEntity<ApiResponse<Void>> deleteProductlike(
@PathVariable("productId") Long productId,
@AuthenticationPrincipal JwtUser jwtUser) {
productLikeService.deleteProductLike(jwtUser.getId(), productId);

return ResponseEntity.ok(ApiResponse.success(SuccessCode.NO_CONTENT));
}
}
8 changes: 7 additions & 1 deletion src/main/java/com/helpmeCookies/product/entity/Like.java
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@
import jakarta.persistence.Id;
import jakarta.persistence.JoinColumn;
import jakarta.persistence.ManyToOne;
import jakarta.persistence.OneToOne;
import jakarta.persistence.Table;

@Entity
Expand All @@ -25,4 +24,11 @@ public class Like {
@ManyToOne
@JoinColumn(name = "product_id")
private Product product;

protected Like(){};

public Like(User user, Product product) {
this.user = user;
this.product = product;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package com.helpmeCookies.product.repository;

import com.helpmeCookies.product.entity.Like;
import com.helpmeCookies.product.entity.Product;
import com.helpmeCookies.user.entity.User;
import java.util.Optional;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;

@Repository
public interface ProductLikeRepository extends JpaRepository<Like, Long> {
Optional<Like> findDistinctFirstByUserAndProduct(User user, Product product);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
package com.helpmeCookies.product.service;

import com.helpmeCookies.product.entity.Like;
import com.helpmeCookies.product.entity.Product;
import com.helpmeCookies.product.repository.ProductLikeRepository;
import com.helpmeCookies.product.repository.ProductRepository;
import com.helpmeCookies.user.entity.User;
import com.helpmeCookies.user.repository.UserRepository;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

@Service
@RequiredArgsConstructor
public class ProductLikeService {
private final ProductLikeRepository productLikeRepository;
private final UserRepository userRepository;
private final ProductRepository productRepository;

@Transactional
public void productLike(Long userId, Long productId) {
User user = userRepository.findById(userId).orElseThrow(() -> new IllegalArgumentException("유효하지 않은 유저Id입니다." + userId));
Product product = productRepository.findById(productId).orElseThrow(() -> new IllegalArgumentException("유효하지 않은 상품Id입니다." + productId));
Like like = new Like(user, product);
productLikeRepository.save(like);
}

@Transactional
public void deleteProductLike(Long userId, Long productId) {
User user = userRepository.findById(userId).orElseThrow(() -> new IllegalArgumentException("유효하지 않은 유저Id입니다." + userId));
Product product = productRepository.findById(productId).orElseThrow(() -> new IllegalArgumentException("유효하지 않은 상품Id입니다." + productId));

Like like = productLikeRepository.findDistinctFirstByUserAndProduct(user,product).orElseThrow(() -> new IllegalArgumentException("존재하지 않는 상품 찜 항목입니다."));
productLikeRepository.delete(like);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@
import com.helpmeCookies.product.dto.ProductPage;
import lombok.RequiredArgsConstructor;
import org.springframework.data.domain.Pageable;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.ZSetOperations;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.bind.annotation.GetMapping;
Expand All @@ -20,10 +22,17 @@ public class ProductService {
private final ProductRepository productRepository;
private final ProductImageRepository productImageRepository;
private final ArtistInfoRepository artistInfoRepository;
private final RedisTemplate<String, Object> redisTemplate;

@Transactional(readOnly = true)
public ProductPage.Paging getProductsByPage(String query, Pageable pageable) {
var productPage = productRepository.findByNameWithIdx(query, pageable);

ZSetOperations<String, Object> zSet = redisTemplate.opsForZSet();
var zSetKey = "search:" + query;
var time = System.currentTimeMillis();
zSet.add(zSetKey, String.valueOf(time), time);

return ProductPage.Paging.from(productPage);
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,11 +1,14 @@
package com.helpmeCookies.review.controller;

import com.helpmeCookies.global.ApiResponse.ApiResponse;
import com.helpmeCookies.global.ApiResponse.SuccessCode;
import com.helpmeCookies.review.dto.ReviewRequest;
import com.helpmeCookies.review.dto.ReviewResponse;
import com.helpmeCookies.review.entity.Review;
import com.helpmeCookies.review.service.ReviewService;
import lombok.RequiredArgsConstructor;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
Expand All @@ -21,25 +24,29 @@ public class ReviewController {

private final ReviewService reviewService;

//TODO 감상평 삭제
//TODO 해당 상품의 감상평 조회
//TODO 내 감상평 조회

@PostMapping("/{productId}")
public ResponseEntity<Void> postReview(@RequestBody ReviewRequest request, @PathVariable Long productId) {
public ResponseEntity<ApiResponse<Void>> postReview(@RequestBody ReviewRequest request, @PathVariable Long productId) {
reviewService.saveReview(request, productId);
return ResponseEntity.ok().build();
return ResponseEntity.ok(ApiResponse.success(SuccessCode.CREATED_SUCCESS));
}

@PutMapping("/{productId}/{reviewId}")
public ResponseEntity<Void> editReview(@RequestBody ReviewRequest request, @PathVariable Long productId, @PathVariable Long reviewId) {
public ResponseEntity<ApiResponse<Void>> editReview(@RequestBody ReviewRequest request, @PathVariable Long productId, @PathVariable Long reviewId) {
reviewService.editReview(request, productId, reviewId);
return ResponseEntity.ok().build();
return ResponseEntity.ok(ApiResponse.success(SuccessCode.CREATED_SUCCESS));
}

@GetMapping("/{reviewId}")
public ResponseEntity<ReviewResponse> getReview(@PathVariable Long reviewId) {
public ResponseEntity<ApiResponse<ReviewResponse>> getReview(@PathVariable Long reviewId) {
Review response = reviewService.getReview(reviewId);
return ResponseEntity.ok(ReviewResponse.fromEntity(response));
return ResponseEntity.ok(ApiResponse.success(SuccessCode.CREATED_SUCCESS,ReviewResponse.fromEntity(response)));
}

@DeleteMapping("/{reviewId}")
public ResponseEntity<ApiResponse<Void>> deleteView(@PathVariable Long reviewId) {
reviewService.deleteReview(reviewId);
return ResponseEntity.ok(ApiResponse.success(SuccessCode.NO_CONTENT));
}
}
Original file line number Diff line number Diff line change
@@ -1,9 +1,19 @@
package com.helpmeCookies.review.repository;

import com.helpmeCookies.product.entity.Product;
import com.helpmeCookies.review.entity.Review;
import com.helpmeCookies.user.entity.ArtistInfo;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.query.Param;
import org.springframework.stereotype.Repository;

@Repository
public interface ReviewRepository extends JpaRepository<Review, Long> {
Page<Review> findAllByProduct(Product product, Pageable pageable);

@Query("SELECT r FROM Review r JOIN r.product p JOIN p.artistInfo a where a = :artistInfo")
Page<Review> findAllByArtistInfo(@Param("artistInfo") ArtistInfo artistInfo, Pageable pageable);
}
27 changes: 27 additions & 0 deletions src/main/java/com/helpmeCookies/review/service/ReviewService.java
Original file line number Diff line number Diff line change
Expand Up @@ -3,33 +3,60 @@
import com.helpmeCookies.product.entity.Product;
import com.helpmeCookies.product.repository.ProductRepository;
import com.helpmeCookies.review.dto.ReviewRequest;
import com.helpmeCookies.review.dto.ReviewResponse;
import com.helpmeCookies.review.entity.Review;
import com.helpmeCookies.review.repository.ReviewRepository;
import com.helpmeCookies.user.entity.ArtistInfo;
import com.helpmeCookies.user.entity.User;
import com.helpmeCookies.user.repository.ArtistInfoRepository;
import com.helpmeCookies.user.repository.UserRepository;
import lombok.RequiredArgsConstructor;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

@Service
@RequiredArgsConstructor
public class ReviewService {
private final ReviewRepository reviewRepository;
private final UserRepository userRepository;
private final ProductRepository productRepository;
private final ArtistInfoRepository artistInfoRepository;

@Transactional
public void saveReview(ReviewRequest request, Long productId) {
User writer = userRepository.findById(request.writerId()).orElseThrow(() -> new IllegalArgumentException("유효하지 않은 writerID입니다."));
Product product = productRepository.findById(productId).orElseThrow(() -> new IllegalArgumentException("유효하지 않은 productID입니다."));

reviewRepository.save(request.toEntity(writer,product));
}

@Transactional
public void editReview(ReviewRequest request, Long productId, Long reviewId) {
Review review = reviewRepository.findById(reviewId).orElseThrow(() -> new IllegalArgumentException("유효하지 않은 reviewId 입니다."));
review.updateContent(request.content());
}

@Transactional(readOnly = true)
public Review getReview(Long reviewId) {
return reviewRepository.findById(reviewId).orElseThrow(() -> new IllegalArgumentException("유효하지 않은 reviewId 입니다."));
}

@Transactional
public void deleteReview(Long reviewId) {
reviewRepository.deleteById(reviewId);
}

@Transactional(readOnly = true)
public Page<ReviewResponse> getAllReviewByProduct(Long productId, Pageable pageable) {
Product product = productRepository.findById(productId).orElseThrow(() -> new IllegalArgumentException("유효하지 않은 productId입니다." + productId));
return reviewRepository.findAllByProduct(product,pageable).map(ReviewResponse::fromEntity);
}

@Transactional(readOnly = true)
public Page<ReviewResponse> getAllReviewByArtist(Long artistId, Pageable pageable) {
ArtistInfo artistInfo = artistInfoRepository.findById(artistId).orElseThrow(() -> new IllegalArgumentException("유효하지 않은 ArtistId입니다." + artistId));
return reviewRepository.findAllByArtistInfo(artistInfo,pageable).map(ReviewResponse::fromEntity);
}
}
Loading

0 comments on commit ccc701b

Please sign in to comment.