From 3d43bf02c6d97f22c8e03cfd247e3ef7f82758db Mon Sep 17 00:00:00 2001 From: GeonWoo Date: Thu, 25 Jan 2024 09:59:27 +0900 Subject: [PATCH] =?UTF-8?q?Feat:=20Review=20CRUD=20=EA=B8=B0=EB=8A=A5=20?= =?UTF-8?q?=EA=B5=AC=ED=98=84,=20Stock=20=EA=B0=9D=EC=B2=B4=20=EC=83=9D?= =?UTF-8?q?=EC=84=B1,=20secret.yml=20=ED=8C=8C=EC=9D=BC=20=EC=83=9D?= =?UTF-8?q?=EC=84=B1=20=EB=B0=8F=20=EC=8A=A4=EC=9B=A8=EA=B1=B0=EB=A5=BC=20?= =?UTF-8?q?=EC=9C=84=ED=95=9C=20@Operation=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .gitignore | 3 + build.gradle | 3 + .../dpang/item/controller/ItemController.java | 40 +++++++------ .../item/controller/ReviewController.java | 59 +++++++++++++++++++ .../kea/dpang/item/dto/ReadCartItemDto.java | 2 +- .../kea/dpang/item/dto/ReviewResponseDto.java | 10 ++++ .../java/kea/dpang/item/entity/Review.java | 20 +++++++ .../exception/ReviewNotFoundException.java | 16 +++++ .../dpang/item/repository/ItemRepository.java | 2 + .../kea/dpang/item/service/ItemService.java | 8 ++- .../dpang/item/service/ItemServiceImpl.java | 8 +-- .../kea/dpang/item/service/ReviewService.java | 36 +++++++++++ .../dpang/item/service/ReviewServiceImpl.java | 56 +++++++++++++++++- src/main/resources/application.properties | 1 - src/main/resources/application.yml | 11 ++++ src/main/resources/secret.yml | 9 +++ use | 0 17 files changed, 257 insertions(+), 27 deletions(-) create mode 100644 src/main/java/kea/dpang/item/exception/ReviewNotFoundException.java delete mode 100644 src/main/resources/application.properties create mode 100644 src/main/resources/application.yml create mode 100644 src/main/resources/secret.yml create mode 100644 use diff --git a/.gitignore b/.gitignore index c2065bc..2581b79 100644 --- a/.gitignore +++ b/.gitignore @@ -35,3 +35,6 @@ out/ ### VS Code ### .vscode/ + +### etc ### +secret.yml diff --git a/build.gradle b/build.gradle index 96a8337..171b36d 100644 --- a/build.gradle +++ b/build.gradle @@ -40,6 +40,9 @@ dependencies { implementation 'org.springdoc:springdoc-openapi-starter-webmvc-ui:2.1.0' implementation 'org.springdoc:springdoc-openapi-starter-webmvc-api:2.1.0' + // MySQL + runtimeOnly 'com.mysql:mysql-connector-j' + } tasks.named('test') { diff --git a/src/main/java/kea/dpang/item/controller/ItemController.java b/src/main/java/kea/dpang/item/controller/ItemController.java index 59361ee..40c84d0 100644 --- a/src/main/java/kea/dpang/item/controller/ItemController.java +++ b/src/main/java/kea/dpang/item/controller/ItemController.java @@ -4,8 +4,10 @@ import kea.dpang.item.dto.*; import kea.dpang.item.entity.Item; import kea.dpang.item.service.ItemServiceImpl; + import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; + import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.*; import org.springframework.web.servlet.support.ServletUriComponentsBuilder; @@ -22,6 +24,20 @@ public class ItemController { private final ItemServiceImpl itemService; + @PostMapping + @Operation(summary = "상품 등록", description = "상품 정보를 시스템에 추가합니다.") + public ResponseEntity createItem(@RequestBody ItemCreateDto itemCreateDto) { + ItemResponseDto item = itemService.createItem(itemCreateDto); + log.info("새로운 상품 등록 완료. 상품 ID: {}", item.getItemId()); + + URI location = ServletUriComponentsBuilder.fromCurrentRequest() + .path("/{itemId}") + .buildAndExpand(item.getItemId()) + .toUri(); + + return ResponseEntity.created(location).body(item); + } + @GetMapping("/{itemId}") @Operation(summary = "상품 상세 정보 조회", description = "상품의 상세 정보를 조회합니다.") public ResponseEntity getItem(@PathVariable Long itemId) { @@ -48,24 +64,10 @@ public ResponseEntity> getPopularItems() { return ResponseEntity.ok(popularItems); } - @PostMapping - @Operation(summary = "상품 등록", description = "상품 정보를 시스템에 추가합니다.") - public ResponseEntity createItem(@RequestBody ItemCreateDto createItemDto) { - ItemResponseDto item = itemService.createItem(createItemDto); - log.info("새로운 상품 등록 완료. 상품 ID: {}", item.getItemId()); - - URI location = ServletUriComponentsBuilder.fromCurrentRequest() - .path("/{itemId}") - .buildAndExpand(item.getItemId()) - .toUri(); - - return ResponseEntity.created(location).body(item); - } - @PutMapping("/{itemId}") @Operation(summary = "상품 수정", description = "기존 상품 정보를 업데이트합니다.") - public ResponseEntity updateItem(@PathVariable Long itemId, @RequestBody ItemUpdateDto updateItemDto) { - ItemResponseDto item = itemService.updateItem(itemId, updateItemDto); + public ResponseEntity updateItem(@PathVariable Long itemId, @RequestBody ItemUpdateDto itemUpdateDto) { + ItemResponseDto item = itemService.updateItem(itemId, itemUpdateDto); log.info("상품 정보 업데이트 완료. 상품 ID: {}", item.getItemId()); return ResponseEntity.ok(item); @@ -89,6 +91,7 @@ public ResponseEntity incrementItemView(@PathVariable Long itemId) { } @GetMapping("/cart") + @Operation(summary = "장바구니 조회", description = "장바구니의 상품 정보 목록을 조회 합니다.") public ResponseEntity> getCartItemInfo(@RequestParam("itemIds") List itemIds) { // 상품 ID 목록에 해당하는 상품을 찾습니다. @@ -104,6 +107,7 @@ public ResponseEntity> getCartItemInfo(@RequestParam("item } @GetMapping("/cart/{itemId}") + @Operation(summary = "장바구니 상품 조회", description = "장바구니의 상품 별 상세 정보를 조회 합니다.") public ResponseEntity getCartItemInfo(@PathVariable("itemId") Long itemId) { // item @@ -115,6 +119,7 @@ public ResponseEntity getCartItemInfo(@PathVariable("itemId") L } @GetMapping("/wishlist") + @Operation(summary = "위시리스트 조회", description = "위시리스트의 상품 정보 목록을 조회합니다.") public ResponseEntity> getWishlistItemInfo(@RequestParam("itemIds") List itemIds) { // 상품 ID 목록에 해당하는 상품을 찾습니다. @@ -129,7 +134,8 @@ public ResponseEntity> getWishlistItemInfo(@RequestPar return ResponseEntity.ok(itemInfoDtos); } - @GetMapping("/cart/{itemId}") + @GetMapping("/wishlist/{itemId}") + @Operation(summary = "위시리스트 상품 조회", description = "위시리스트의 상품 별 상세 정보를 조회합니다.") public ResponseEntity getWishlistItemInfo(@PathVariable("itemId") Long itemId) { // 상품 ID 목록에 해당하는 상품을 찾습니다. diff --git a/src/main/java/kea/dpang/item/controller/ReviewController.java b/src/main/java/kea/dpang/item/controller/ReviewController.java index 12979d0..381b765 100644 --- a/src/main/java/kea/dpang/item/controller/ReviewController.java +++ b/src/main/java/kea/dpang/item/controller/ReviewController.java @@ -1,4 +1,63 @@ package kea.dpang.item.controller; +import io.swagger.v3.oas.annotations.Operation; +import kea.dpang.item.dto.*; +import kea.dpang.item.service.ReviewServiceImpl; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; + +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.*; +import org.springframework.web.servlet.support.ServletUriComponentsBuilder; + +import java.net.URI; + +@RestController +@RequiredArgsConstructor +@RequestMapping("/reviews") +@Slf4j public class ReviewController { + + private final ReviewServiceImpl reviewService; + + @PostMapping + @Operation(summary = "리뷰 등록", description = "리뷰 정보를 시스템에 추가합니다.") + public ResponseEntity createReview(@RequestBody ReviewCreateDto reviewCreateDto) { + ReviewResponseDto review = reviewService.createReview(reviewCreateDto); + log.info("새로운 리뷰 등록 완료. 리뷰 ID: {}", review.getReviewId()); + + URI location = ServletUriComponentsBuilder.fromCurrentRequest() + .path("/{reviewId}") + .buildAndExpand(review.getReviewId()) + .toUri(); + + return ResponseEntity.created(location).body(review); + } + + @GetMapping("/{reviewId}") + @Operation(summary = "리뷰 조회", description = "리뷰를 조회합니다.") + public ResponseEntity getReview(@PathVariable Long reviewId) { + ReviewResponseDto review = reviewService.getReview(reviewId); + log.info("리뷰 정보 조회 완료. 리뷰 ID: {}", review.getReviewId()); + + return ResponseEntity.ok(review); + } + + @PutMapping("/{reviewId}") + @Operation(summary = "리뷰 수정", description = "기존 리뷰 정보를 업데이트합니다.") + public ResponseEntity updateReview(@PathVariable Long reviewId, @RequestBody ReviewUpdateDto reviewUpdateDto) { + ReviewResponseDto review = reviewService.updateReview(reviewId, reviewUpdateDto); + log.info("리뷰 정보 업데이트 완료. 리뷰 ID: {}", review.getReviewId()); + + return ResponseEntity.ok(review); + } + + @DeleteMapping("/{reviewId}") + @Operation(summary = "리뷰 삭제", description = "리뷰 정보를 시스템에서 제거합니다.") + public ResponseEntity deleteReview(@PathVariable Long reviewId) { + reviewService.deleteReview(reviewId); + log.info("리뷰 삭제 완료. 리뷰 ID: {}", reviewId); + + return ResponseEntity.noContent().build(); + } } diff --git a/src/main/java/kea/dpang/item/dto/ReadCartItemDto.java b/src/main/java/kea/dpang/item/dto/ReadCartItemDto.java index 41ec9cd..fe42d45 100644 --- a/src/main/java/kea/dpang/item/dto/ReadCartItemDto.java +++ b/src/main/java/kea/dpang/item/dto/ReadCartItemDto.java @@ -6,7 +6,7 @@ public class ReadCartItemDto { private final String thumbnailImage; // 상품 이미지 URL private final String itemName; // 상품 이름 - private final Long discountPrice; // 상품 가격 + private final int discountPrice; // 상품 가격 public ReadCartItemDto(Item item) { this.thumbnailImage = item.getThumbnailImage(); diff --git a/src/main/java/kea/dpang/item/dto/ReviewResponseDto.java b/src/main/java/kea/dpang/item/dto/ReviewResponseDto.java index e1b6c1e..124aa52 100644 --- a/src/main/java/kea/dpang/item/dto/ReviewResponseDto.java +++ b/src/main/java/kea/dpang/item/dto/ReviewResponseDto.java @@ -1,5 +1,7 @@ package kea.dpang.item.dto; +import kea.dpang.item.entity.Item; +import kea.dpang.item.entity.Review; import lombok.AllArgsConstructor; import lombok.Data; @@ -11,5 +13,13 @@ public class ReviewResponseDto { private Long itemId; private String content; private Double rating; + + public ReviewResponseDto(Review review) { + this.reviewId = review.getReviewId(); + this.reviewerId = review.getReviewerId(); + this.itemId = review.getItemId(); + this.content = review.getContent(); + this.rating = review.getRating(); + } } diff --git a/src/main/java/kea/dpang/item/entity/Review.java b/src/main/java/kea/dpang/item/entity/Review.java index 85c31ed..7269892 100644 --- a/src/main/java/kea/dpang/item/entity/Review.java +++ b/src/main/java/kea/dpang/item/entity/Review.java @@ -2,6 +2,10 @@ import jakarta.persistence.*; import kea.dpang.item.base.BaseEntity; +import kea.dpang.item.dto.ItemCreateDto; +import kea.dpang.item.dto.ItemUpdateDto; +import kea.dpang.item.dto.ReviewCreateDto; +import kea.dpang.item.dto.ReviewUpdateDto; import lombok.*; @Getter @@ -32,4 +36,20 @@ public class Review extends BaseEntity { // 평점 @Column(name = "rating", nullable = false) private Double rating; + + public static Review from(ReviewCreateDto dto) { + return Review.builder() + .reviewerId(dto.getReviewerId()) + .itemId(dto.getItemId()) + .content(dto.getContent()) + .rating(dto.getRating()) + .build(); + } + + public void updateInformation(ReviewUpdateDto dto) { + this.reviewId = dto.getReviewId(); + this.content = dto.getContent(); + this.rating = dto.getRating(); + } + } diff --git a/src/main/java/kea/dpang/item/exception/ReviewNotFoundException.java b/src/main/java/kea/dpang/item/exception/ReviewNotFoundException.java new file mode 100644 index 0000000..2db0849 --- /dev/null +++ b/src/main/java/kea/dpang/item/exception/ReviewNotFoundException.java @@ -0,0 +1,16 @@ +package kea.dpang.item.exception; + +import lombok.Getter; +import org.springframework.http.HttpStatus; +import org.springframework.web.bind.annotation.ResponseStatus; + +@Getter +@ResponseStatus(value = HttpStatus.NOT_FOUND) +public class ReviewNotFoundException extends RuntimeException { + private final Long reviewId; + + public ReviewNotFoundException(Long reviewId) { + super(String.format("상품을 찾을 수 없음: 상품 ID - '%s'", reviewId)); + this.reviewId = reviewId; + } +} \ No newline at end of file diff --git a/src/main/java/kea/dpang/item/repository/ItemRepository.java b/src/main/java/kea/dpang/item/repository/ItemRepository.java index beca24d..2fb7493 100644 --- a/src/main/java/kea/dpang/item/repository/ItemRepository.java +++ b/src/main/java/kea/dpang/item/repository/ItemRepository.java @@ -3,6 +3,8 @@ import kea.dpang.item.entity.Item; import org.springframework.data.jpa.repository.JpaRepository; +import java.util.List; + public interface ItemRepository extends JpaRepository { List findCartItemsByItemId(List itemId); diff --git a/src/main/java/kea/dpang/item/service/ItemService.java b/src/main/java/kea/dpang/item/service/ItemService.java index bfecaf8..d01541a 100644 --- a/src/main/java/kea/dpang/item/service/ItemService.java +++ b/src/main/java/kea/dpang/item/service/ItemService.java @@ -1,8 +1,10 @@ package kea.dpang.item.service; -import kea.dpang.item.dto.CreateItemDto; -import kea.dpang.item.dto.ItemDetailDto; -import kea.dpang.item.dto.UpdateItemDto; +import kea.dpang.item.dto.ItemCreateDto; +import kea.dpang.item.dto.ItemResponseDto; +import kea.dpang.item.dto.ItemThumbnailDto; +import kea.dpang.item.dto.ItemUpdateDto; +import kea.dpang.item.dto.PopularItemDto; import kea.dpang.item.entity.Item; import java.util.List; diff --git a/src/main/java/kea/dpang/item/service/ItemServiceImpl.java b/src/main/java/kea/dpang/item/service/ItemServiceImpl.java index 2ef0cf4..a66cb59 100644 --- a/src/main/java/kea/dpang/item/service/ItemServiceImpl.java +++ b/src/main/java/kea/dpang/item/service/ItemServiceImpl.java @@ -4,8 +4,9 @@ import kea.dpang.item.entity.Item; import kea.dpang.item.exception.ItemNotFoundException; import kea.dpang.item.repository.ItemRepository; -import kea.dpang.item.repository.ReviewRepository; + import lombok.RequiredArgsConstructor; + import org.springframework.data.redis.core.StringRedisTemplate; import org.springframework.data.redis.core.ValueOperations; import org.springframework.data.redis.core.ZSetOperations; @@ -21,7 +22,6 @@ public class ItemServiceImpl implements ItemService { private final ItemRepository itemRepository; - private final ReviewRepository reviewRepository; private static final String ITEM_VIEW_COUNT_KEY = "item:viewCount"; private final StringRedisTemplate redisTemplate; @@ -101,8 +101,8 @@ public void deleteItem(Long itemId) { // 상품 정보 목록 조회 @Override - public List getCartItems(List itemId) { - return itemRepository.findCartItemsByItemId(itemId); + public List getCartItems(List itemIds) { + return itemRepository.findCartItemsByItemId(itemIds); } // 상품 정보 조회 diff --git a/src/main/java/kea/dpang/item/service/ReviewService.java b/src/main/java/kea/dpang/item/service/ReviewService.java index 4360a9d..f2d93d9 100644 --- a/src/main/java/kea/dpang/item/service/ReviewService.java +++ b/src/main/java/kea/dpang/item/service/ReviewService.java @@ -1,4 +1,40 @@ package kea.dpang.item.service; +import kea.dpang.item.dto.ReviewCreateDto; +import kea.dpang.item.dto.ReviewResponseDto; +import kea.dpang.item.dto.ReviewUpdateDto; +import kea.dpang.item.entity.Review; + public interface ReviewService { + /** + * 주어진 ID에 해당하는 리뷰의 정보를 조회합니다. + * + * @param reviewId 조회할 리뷰의 ID + * @return 조회된 리뷰의 정보가 담긴 Detail DTO + */ + ReviewResponseDto getReview(Long reviewId); + + /** + * 새로운 리뷰을 등록합니다. + * + * @param reviewCreateDto 등록할 리뷰의 정보가 담긴 DTO + * @return 등록된 리뷰의 정보가 담긴 Detail DTO + */ + ReviewResponseDto createReview(ReviewCreateDto reviewCreateDto); + + /** + * 리뷰의 정보를 업데이트합니다. + * + * @param reviewId 업데이트할 리뷰의 ID + * @param reviewUpdateDto 업데이트할 리뷰의 정보가 담긴 DTO + * @return 업데이트된 리뷰의 정보가 담긴 Detail DTO + */ + ReviewResponseDto updateReview(Long reviewId, ReviewUpdateDto reviewUpdateDto); + + /** + * 주어진 ID에 해당하는 리뷰을 삭제합니다. + * + * @param reviewId 삭제할 리뷰의 ID + */ + void deleteReview(Long reviewId); } diff --git a/src/main/java/kea/dpang/item/service/ReviewServiceImpl.java b/src/main/java/kea/dpang/item/service/ReviewServiceImpl.java index 1def2cc..8b2e6c2 100644 --- a/src/main/java/kea/dpang/item/service/ReviewServiceImpl.java +++ b/src/main/java/kea/dpang/item/service/ReviewServiceImpl.java @@ -1,4 +1,58 @@ package kea.dpang.item.service; -public class ReviewServiceImpl { +import kea.dpang.item.dto.*; +import kea.dpang.item.entity.Review; +import kea.dpang.item.exception.ReviewNotFoundException; +import kea.dpang.item.repository.ReviewRepository; + +import lombok.RequiredArgsConstructor; + +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +@Service +@RequiredArgsConstructor +public class ReviewServiceImpl implements ReviewService { + + private final ReviewRepository reviewRepository; + + // 리뷰 등록 + @Override + @Transactional + public ReviewResponseDto createReview(ReviewCreateDto dto) { + Review review = Review.from(dto); + return new ReviewResponseDto(reviewRepository.save(review)); + } + + // 리뷰 조회 + @Override + @Transactional(readOnly = true) + public ReviewResponseDto getReview(Long reviewId) { + return reviewRepository.findById(reviewId) + .map(ReviewResponseDto::new) + .orElseThrow(() -> new ReviewNotFoundException(reviewId)); + } + + // 리뷰 수정 + @Override + @Transactional + public ReviewResponseDto updateReview(Long reviewId, ReviewUpdateDto dto) { + Review review = reviewRepository.findById(reviewId) + .orElseThrow(() -> new ReviewNotFoundException(reviewId)); + + review.updateInformation(dto); + return new ReviewResponseDto(reviewRepository.save(review)); + } + + // 리뷰 삭제 + @Override + @Transactional + public void deleteReview(Long reviewId) { + Review review = reviewRepository.findById(reviewId) + .orElseThrow(() -> new ReviewNotFoundException(reviewId)); + reviewRepository.delete(review); + } + + + } diff --git a/src/main/resources/application.properties b/src/main/resources/application.properties deleted file mode 100644 index 8b13789..0000000 --- a/src/main/resources/application.properties +++ /dev/null @@ -1 +0,0 @@ - diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml new file mode 100644 index 0000000..c938c7a --- /dev/null +++ b/src/main/resources/application.yml @@ -0,0 +1,11 @@ +spring: + config: + import: classpath:/secret.yml + + datasource: + driver-class-name: com.mysql.cj.jdbc.Driver + url: jdbc:mysql://localhost:3306/itemdb + + jpa: + hibernate: + ddl-auto: update \ No newline at end of file diff --git a/src/main/resources/secret.yml b/src/main/resources/secret.yml new file mode 100644 index 0000000..2da4118 --- /dev/null +++ b/src/main/resources/secret.yml @@ -0,0 +1,9 @@ +spring: + datasource: + username: root + password: 12345 + +eureka: + client: + service-url: + defaultZone: http://172.16.0.156:8761/eureka \ No newline at end of file diff --git a/use b/use new file mode 100644 index 0000000..e69de29