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/reservation/dto/request/ReservationRequest.java b/src/main/java/poomasi/domain/reservation/dto/request/ReservationRequest.java index e8e74630..4ffdf108 100644 --- a/src/main/java/poomasi/domain/reservation/dto/request/ReservationRequest.java +++ b/src/main/java/poomasi/domain/reservation/dto/request/ReservationRequest.java @@ -14,7 +14,7 @@ public record ReservationRequest( Long memberId, LocalDate reservationDate, - int reservationCount, + int memberCount, String request ) { public Reservation toEntity(Member member, Farm farm, FarmSchedule farmSchedule) { @@ -23,7 +23,7 @@ public Reservation toEntity(Member member, Farm farm, FarmSchedule farmSchedule) .farm(farm) .scheduleId(farmSchedule) .reservationDate(reservationDate) - .reservationCount(reservationCount) + .memberCount(memberCount) .request(request) .build(); } diff --git a/src/main/java/poomasi/domain/reservation/entity/Reservation.java b/src/main/java/poomasi/domain/reservation/entity/Reservation.java index 68b47833..87a353bb 100644 --- a/src/main/java/poomasi/domain/reservation/entity/Reservation.java +++ b/src/main/java/poomasi/domain/reservation/entity/Reservation.java @@ -30,20 +30,17 @@ public class Reservation { @Comment("농장") @OneToOne(fetch = FetchType.LAZY) - @JoinColumn(name = "farm_id") - @Column(nullable = false) + @JoinColumn(name = "farm_id", nullable = false) private Farm farm; @Comment("예약자") @OneToOne(fetch = FetchType.LAZY) - @JoinColumn(name = "user_id") - @Column(nullable = false) + @JoinColumn(name = "member_id", nullable = false) private Member member; @Comment("예약 시간") @OneToOne(fetch = FetchType.LAZY) - @JoinColumn(name = "schedule_id") - @Column(nullable = false) + @JoinColumn(name = "schedule_id", nullable = false) private FarmSchedule scheduleId; @Comment("예약 날짜") 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/BusinessError.java b/src/main/java/poomasi/global/error/BusinessError.java index 38c31bf1..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;