From 6a56050ed3f6aad53d2c449d343b1b24b187dd21 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=A0=95=EC=A7=80=EB=AF=BC?= <108014449+stopmin@users.noreply.github.com> Date: Fri, 11 Oct 2024 18:27:54 +0900 Subject: [PATCH] =?UTF-8?q?=EC=9C=84=EC=8B=9C=EB=A6=AC=EC=8A=A4=ED=8A=B8?= =?UTF-8?q?=EB=A5=BC=20=EC=83=9D=EC=84=B1=ED=95=9C=EB=8B=A4=20(#58)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * feat: product review 생성 조회 * feat: category - product 연관관계 추가 * feat: 카테고리 내 상품 조회 * fix: 빠진 파일 추가 * style: 아 맞다 정렬 * feat: 상품리뷰 - 상품리뷰사진 연관 관계 생성 * refactor: 평균 평점을 리뷰 추가할 때 계산하도록 변경 * feat: 충돌 해결 * feat: 수정사항 삭제 * feat: review 생성 * style: 아 맞다 정렬 * feature: 위시리스트 등록 - 재고와 무관하게 경우 위시리스트 가능하게 #57 * feature: 위시리스트 조회 #57 --------- Co-authored-by: canyos <4581974@naver.com> Co-authored-by: canyos <31244128+canyos@users.noreply.github.com> --- .../poomasi/domain/member/entity/Member.java | 6 +++ .../product/service/ProductService.java | 16 ++++++- .../WishListPlatformController.java | 33 +++++++++++++ .../wishlist/dto/WishListDeleteRequest.java | 7 +++ .../domain/wishlist/dto/WishListResponse.java | 21 ++++++++ .../dto/request/WishListAddRequest.java | 17 +++++++ .../domain/wishlist/entity/WishList.java | 48 +++++++++++++++++++ .../repository/WishListRepository.java | 12 +++++ .../service/WishListPlatformService.java | 34 +++++++++++++ .../wishlist/service/WishListService.java | 40 ++++++++++++++++ .../global/error/ApplicationException.java | 1 + .../poomasi/global/error/BusinessError.java | 5 +- .../global/error/BusinessException.java | 1 + 13 files changed, 239 insertions(+), 2 deletions(-) create mode 100644 src/main/java/poomasi/domain/wishlist/controller/WishListPlatformController.java create mode 100644 src/main/java/poomasi/domain/wishlist/dto/WishListDeleteRequest.java create mode 100644 src/main/java/poomasi/domain/wishlist/dto/WishListResponse.java create mode 100644 src/main/java/poomasi/domain/wishlist/dto/request/WishListAddRequest.java create mode 100644 src/main/java/poomasi/domain/wishlist/entity/WishList.java create mode 100644 src/main/java/poomasi/domain/wishlist/repository/WishListRepository.java create mode 100644 src/main/java/poomasi/domain/wishlist/service/WishListPlatformService.java create mode 100644 src/main/java/poomasi/domain/wishlist/service/WishListService.java diff --git a/src/main/java/poomasi/domain/member/entity/Member.java b/src/main/java/poomasi/domain/member/entity/Member.java index bbf54171..3ca82998 100644 --- a/src/main/java/poomasi/domain/member/entity/Member.java +++ b/src/main/java/poomasi/domain/member/entity/Member.java @@ -6,7 +6,10 @@ import lombok.NoArgsConstructor; import lombok.Setter; import org.hibernate.annotations.SQLDelete; +import poomasi.domain.wishlist.entity.WishList; + import java.time.LocalDateTime; +import java.util.List; @Getter @Entity @@ -41,6 +44,9 @@ public class Member { @OneToOne(mappedBy = "member", cascade = CascadeType.ALL, fetch = FetchType.LAZY) private MemberProfile memberProfile; + @OneToMany(mappedBy = "member", cascade = CascadeType.ALL, fetch = FetchType.LAZY) + private List wishLists; + private LocalDateTime deletedAt; public Member(String email, String password, LoginType loginType, Role role) { diff --git a/src/main/java/poomasi/domain/product/service/ProductService.java b/src/main/java/poomasi/domain/product/service/ProductService.java index 765a18b5..ca7b482c 100644 --- a/src/main/java/poomasi/domain/product/service/ProductService.java +++ b/src/main/java/poomasi/domain/product/service/ProductService.java @@ -1,9 +1,11 @@ package poomasi.domain.product.service; import java.util.List; + import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Service; import poomasi.domain.product.dto.ProductResponse; +import poomasi.domain.product.entity.Product; import poomasi.domain.product.repository.ProductRepository; import poomasi.global.error.BusinessError; import poomasi.global.error.BusinessException; @@ -22,8 +24,20 @@ public List getAllProducts() { } public ProductResponse getProductByProductId(Long productId) { + return ProductResponse.fromEntity(findProductById(productId)); + } + + + public Product findValidProductById(Long productId) { + Product product = findProductById(productId); + if (product.getStock() == 0) { + throw new BusinessException(BusinessError.PRODUCT_STOCK_ZERO); + } + return product; + } + + public Product findProductById(Long productId) { return productRepository.findByIdAndDeletedAtIsNull(productId) - .map(ProductResponse::fromEntity) .orElseThrow(() -> new BusinessException(BusinessError.PRODUCT_NOT_FOUND)); } } diff --git a/src/main/java/poomasi/domain/wishlist/controller/WishListPlatformController.java b/src/main/java/poomasi/domain/wishlist/controller/WishListPlatformController.java new file mode 100644 index 00000000..f7b228e6 --- /dev/null +++ b/src/main/java/poomasi/domain/wishlist/controller/WishListPlatformController.java @@ -0,0 +1,33 @@ +package poomasi.domain.wishlist.controller; + +import lombok.RequiredArgsConstructor; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.*; +import poomasi.domain.wishlist.dto.WishListDeleteRequest; +import poomasi.domain.wishlist.dto.request.WishListAddRequest; +import poomasi.domain.wishlist.service.WishListPlatformService; + +@RequestMapping("/api/v1/wish-list") +@RestController +@RequiredArgsConstructor +public class WishListPlatformController { + private final WishListPlatformService wishListPlatformService; + + @PostMapping("/add") + public ResponseEntity addWishList(@RequestBody WishListAddRequest request) { + wishListPlatformService.addWishList(request); + return ResponseEntity.ok().build(); + } + + @PostMapping("/delete") + public ResponseEntity deleteWishList(@RequestBody WishListDeleteRequest request) { + wishListPlatformService.deleteWishList(request); + return ResponseEntity.ok().build(); + } + + @GetMapping("/find") + public ResponseEntity findWishListByMemberId(@RequestBody Long memberId) { + // FIXME : memberID는 SecurityContextHolder에서 가져오도록 수정 + return ResponseEntity.ok(wishListPlatformService.findWishListByMemberId(memberId)); + } +} diff --git a/src/main/java/poomasi/domain/wishlist/dto/WishListDeleteRequest.java b/src/main/java/poomasi/domain/wishlist/dto/WishListDeleteRequest.java new file mode 100644 index 00000000..9651bd52 --- /dev/null +++ b/src/main/java/poomasi/domain/wishlist/dto/WishListDeleteRequest.java @@ -0,0 +1,7 @@ +package poomasi.domain.wishlist.dto; + +public record WishListDeleteRequest( + Long memberId, + Long productId +) { +} diff --git a/src/main/java/poomasi/domain/wishlist/dto/WishListResponse.java b/src/main/java/poomasi/domain/wishlist/dto/WishListResponse.java new file mode 100644 index 00000000..954b50de --- /dev/null +++ b/src/main/java/poomasi/domain/wishlist/dto/WishListResponse.java @@ -0,0 +1,21 @@ +package poomasi.domain.wishlist.dto; + +import poomasi.domain.wishlist.entity.WishList; + +public record WishListResponse( + Long productId, + String productName, + Long price, + String imageUrl, + String description +) { + public static WishListResponse fromEntity(WishList wishList) { + return new WishListResponse( + wishList.getProduct().getId(), + wishList.getProduct().getName(), + wishList.getProduct().getPrice(), + wishList.getProduct().getImageUrl(), + wishList.getProduct().getDescription() + ); + } +} diff --git a/src/main/java/poomasi/domain/wishlist/dto/request/WishListAddRequest.java b/src/main/java/poomasi/domain/wishlist/dto/request/WishListAddRequest.java new file mode 100644 index 00000000..2783a879 --- /dev/null +++ b/src/main/java/poomasi/domain/wishlist/dto/request/WishListAddRequest.java @@ -0,0 +1,17 @@ +package poomasi.domain.wishlist.dto.request; + +import poomasi.domain.member.entity.Member; +import poomasi.domain.product.entity.Product; +import poomasi.domain.wishlist.entity.WishList; + +public record WishListAddRequest( + Long memberId, + Long productId +) { + public WishList toEntity(Member member, Product product) { + return WishList.builder() + .member(member) + .product(product) + .build(); + } +} diff --git a/src/main/java/poomasi/domain/wishlist/entity/WishList.java b/src/main/java/poomasi/domain/wishlist/entity/WishList.java new file mode 100644 index 00000000..8b0ce31b --- /dev/null +++ b/src/main/java/poomasi/domain/wishlist/entity/WishList.java @@ -0,0 +1,48 @@ +package poomasi.domain.wishlist.entity; + +import jakarta.persistence.*; +import lombok.Builder; +import lombok.Getter; +import lombok.NoArgsConstructor; +import org.hibernate.annotations.Comment; +import org.hibernate.annotations.CurrentTimestamp; +import org.hibernate.annotations.SQLDelete; +import org.hibernate.annotations.Where; +import poomasi.domain.member.entity.Member; +import poomasi.domain.product.entity.Product; + +import java.time.LocalDateTime; + +@Entity +@Getter +@NoArgsConstructor +@SQLDelete(sql = "UPDATE product SET deleted_at = current_timestamp WHERE id = ?") +public class WishList { + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private Long id; + + @Comment("회원") + @ManyToOne(fetch = FetchType.LAZY) + @JoinColumn(name = "member_id") + private Member member; + + @Comment("상품") + @ManyToOne(fetch = FetchType.LAZY) + @JoinColumn(name = "product_id") + private Product product; + + @Comment("등록일시") + @CurrentTimestamp + private LocalDateTime createdAt; + + @Comment("삭제일시") + private LocalDateTime deletedAt; + + @Builder + public WishList(Member member, Product product) { + this.member = member; + this.product = product; + } + +} diff --git a/src/main/java/poomasi/domain/wishlist/repository/WishListRepository.java b/src/main/java/poomasi/domain/wishlist/repository/WishListRepository.java new file mode 100644 index 00000000..1a7584b6 --- /dev/null +++ b/src/main/java/poomasi/domain/wishlist/repository/WishListRepository.java @@ -0,0 +1,12 @@ +package poomasi.domain.wishlist.repository; + +import org.springframework.data.jpa.repository.JpaRepository; +import poomasi.domain.wishlist.entity.WishList; + +import java.util.List; + +public interface WishListRepository extends JpaRepository { + List findByMemberId(Long memberId); + + void deleteByMemberIdAndProductId(Long memberId, Long productId); +} diff --git a/src/main/java/poomasi/domain/wishlist/service/WishListPlatformService.java b/src/main/java/poomasi/domain/wishlist/service/WishListPlatformService.java new file mode 100644 index 00000000..fc22b570 --- /dev/null +++ b/src/main/java/poomasi/domain/wishlist/service/WishListPlatformService.java @@ -0,0 +1,34 @@ +package poomasi.domain.wishlist.service; + +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; +import poomasi.domain.wishlist.dto.WishListDeleteRequest; +import poomasi.domain.wishlist.dto.WishListResponse; +import poomasi.domain.wishlist.dto.request.WishListAddRequest; +import poomasi.domain.wishlist.entity.WishList; + +import java.util.List; + +@RequiredArgsConstructor +@Service +public class WishListPlatformService { + private final WishListService wishListService; + + @Transactional + public void addWishList(WishListAddRequest request) { + wishListService.addWishList(request); + } + + @Transactional + public void deleteWishList(WishListDeleteRequest request) { + wishListService.deleteWishList(request); + } + + @Transactional(readOnly = true) + public List findWishListByMemberId(Long memberId) { + return wishListService.findWishListByMemberId(memberId).stream() + .map(WishListResponse::fromEntity) + .toList(); + } +} diff --git a/src/main/java/poomasi/domain/wishlist/service/WishListService.java b/src/main/java/poomasi/domain/wishlist/service/WishListService.java new file mode 100644 index 00000000..a9cf39fa --- /dev/null +++ b/src/main/java/poomasi/domain/wishlist/service/WishListService.java @@ -0,0 +1,40 @@ +package poomasi.domain.wishlist.service; + +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; +import poomasi.domain.member.entity.Member; +import poomasi.domain.member.service.MemberService; +import poomasi.domain.product.entity.Product; +import poomasi.domain.product.service.ProductService; +import poomasi.domain.wishlist.dto.WishListDeleteRequest; +import poomasi.domain.wishlist.dto.request.WishListAddRequest; +import poomasi.domain.wishlist.entity.WishList; +import poomasi.domain.wishlist.repository.WishListRepository; + +import java.util.List; + +@Service +@RequiredArgsConstructor +public class WishListService { + private final WishListRepository wishListRepository; + private final MemberService memberService; + private final ProductService productService; + + @Transactional + public void addWishList(WishListAddRequest request) { + Member member = memberService.findMemberById(request.memberId()); + Product product = productService.findProductById(request.productId()); + wishListRepository.save(request.toEntity(member, product)); + } + + @Transactional + public void deleteWishList(WishListDeleteRequest request) { + wishListRepository.deleteByMemberIdAndProductId(request.memberId(), request.productId()); + } + + @Transactional(readOnly = true) + public List findWishListByMemberId(Long memberId) { + return wishListRepository.findByMemberId(memberId); + } +} diff --git a/src/main/java/poomasi/global/error/ApplicationException.java b/src/main/java/poomasi/global/error/ApplicationException.java index f851d67c..c4f02647 100644 --- a/src/main/java/poomasi/global/error/ApplicationException.java +++ b/src/main/java/poomasi/global/error/ApplicationException.java @@ -6,5 +6,6 @@ @Getter @AllArgsConstructor public class ApplicationException extends RuntimeException { + private final ApplicationError applicationError; } diff --git a/src/main/java/poomasi/global/error/BusinessError.java b/src/main/java/poomasi/global/error/BusinessError.java index 1d6d5f40..d9ce696a 100644 --- a/src/main/java/poomasi/global/error/BusinessError.java +++ b/src/main/java/poomasi/global/error/BusinessError.java @@ -10,10 +10,14 @@ public enum BusinessError { // Product PRODUCT_NOT_FOUND(HttpStatus.NOT_FOUND, "상품을 찾을 수 없습니다."), + PRODUCT_STOCK_ZERO(HttpStatus.BAD_REQUEST, "재고가 없습니다."), // Category CATEGORY_NOT_FOUND(HttpStatus.NOT_FOUND, "카테고리를 찾을 수 없습니다."), + // Review + REVIEW_NOT_FOUND(HttpStatus.NOT_FOUND, "리뷰를 찾을 수 없습니다."), + // Member MEMBER_NOT_FOUND(HttpStatus.NOT_FOUND, "회원이 존재하지 않습니다."), DUPLICATE_MEMBER_EMAIL(HttpStatus.CONFLICT, "중복된 이메일입니다."), @@ -47,7 +51,6 @@ public enum BusinessError { // ETC START_DATE_SHOULD_BE_BEFORE_END_DATE(HttpStatus.BAD_REQUEST, "시작 날짜는 종료 날짜보다 이전이어야 합니다."); - private final HttpStatus httpStatus; private final String message; diff --git a/src/main/java/poomasi/global/error/BusinessException.java b/src/main/java/poomasi/global/error/BusinessException.java index 0bf9077b..170c7344 100644 --- a/src/main/java/poomasi/global/error/BusinessException.java +++ b/src/main/java/poomasi/global/error/BusinessException.java @@ -6,5 +6,6 @@ @AllArgsConstructor @Getter public class BusinessException extends RuntimeException { + private final BusinessError businessError; }