From 9263cfa3b30ee711dc5ce8992569e5bef9f97da8 Mon Sep 17 00:00:00 2001 From: juhabae Date: Thu, 21 Nov 2024 00:31:51 +0900 Subject: [PATCH 1/3] =?UTF-8?q?[Feat]=20MemberCoupon=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../controller/CouponHistoryController.java | 31 +++++++ .../controller/MemberCouponController.java | 54 ++++++++++-- .../coupon/domain/CouponHistory.java | 1 - .../couponset/coupon/domain/MemberCoupon.java | 19 ++-- .../dto/mapper/CouponHistoryMapper.java | 18 ++++ .../coupon/dto/mapper/CouponMapper.java | 31 ++++++- .../dto/response/CouponHistoryResponse.java | 23 ++--- .../dto/response/MemberCouponResponse.java | 8 +- .../MemberCouponNotFoundException.java | 8 ++ .../repository/CouponHistoryRepository.java | 3 +- .../coupon/service/CouponHistoryService.java | 9 ++ .../coupon/service/MemberCouponService.java | 5 +- .../impl/CouponHistoryServiceImpl.java | 25 ++++++ .../service/impl/MemberCouponServiceImpl.java | 87 ++++++++++++++----- .../exception/MemberNotFoundException.java | 9 +- 15 files changed, 270 insertions(+), 61 deletions(-) create mode 100644 src/main/java/com/nhnacademy/heukbaekbookshop/couponset/coupon/controller/CouponHistoryController.java create mode 100644 src/main/java/com/nhnacademy/heukbaekbookshop/couponset/coupon/dto/mapper/CouponHistoryMapper.java create mode 100644 src/main/java/com/nhnacademy/heukbaekbookshop/couponset/coupon/exception/MemberCouponNotFoundException.java create mode 100644 src/main/java/com/nhnacademy/heukbaekbookshop/couponset/coupon/service/CouponHistoryService.java create mode 100644 src/main/java/com/nhnacademy/heukbaekbookshop/couponset/coupon/service/impl/CouponHistoryServiceImpl.java diff --git a/src/main/java/com/nhnacademy/heukbaekbookshop/couponset/coupon/controller/CouponHistoryController.java b/src/main/java/com/nhnacademy/heukbaekbookshop/couponset/coupon/controller/CouponHistoryController.java new file mode 100644 index 00000000..74c58525 --- /dev/null +++ b/src/main/java/com/nhnacademy/heukbaekbookshop/couponset/coupon/controller/CouponHistoryController.java @@ -0,0 +1,31 @@ +package com.nhnacademy.heukbaekbookshop.couponset.coupon.controller; + +import com.nhnacademy.heukbaekbookshop.couponset.coupon.dto.response.CouponHistoryResponse; +import com.nhnacademy.heukbaekbookshop.couponset.coupon.service.CouponHistoryService; +import lombok.RequiredArgsConstructor; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.Pageable; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.*; + +@RestController +@RequestMapping("/api/coupons/histories") +@RequiredArgsConstructor +public class CouponHistoryController { + private final CouponHistoryService couponHistoryService; + + /** + * 사용자 쿠폰 사용 내역 조회 + * @param memberId 사용자 ID + * @param pageable 페이징 객체 + * @return 쿠폰 사용 내역 + */ + @GetMapping("/members/{memberId}") + public ResponseEntity> getCouponHistoriesByUser( + @PathVariable Long memberId, + Pageable pageable) { + Page responses = couponHistoryService.getCouponHistoryByCustomerId(memberId, pageable); + return ResponseEntity.ok(responses); + } +} + diff --git a/src/main/java/com/nhnacademy/heukbaekbookshop/couponset/coupon/controller/MemberCouponController.java b/src/main/java/com/nhnacademy/heukbaekbookshop/couponset/coupon/controller/MemberCouponController.java index 7c2a9665..22d53d12 100644 --- a/src/main/java/com/nhnacademy/heukbaekbookshop/couponset/coupon/controller/MemberCouponController.java +++ b/src/main/java/com/nhnacademy/heukbaekbookshop/couponset/coupon/controller/MemberCouponController.java @@ -1,17 +1,61 @@ package com.nhnacademy.heukbaekbookshop.couponset.coupon.controller; -import com.nhnacademy.heukbaekbookshop.couponset.coupon.service.CouponService; +import com.nhnacademy.heukbaekbookshop.couponset.coupon.dto.response.CouponHistoryResponse; +import com.nhnacademy.heukbaekbookshop.couponset.coupon.dto.response.MemberCouponResponse; +import com.nhnacademy.heukbaekbookshop.couponset.coupon.service.MemberCouponService; import lombok.RequiredArgsConstructor; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.Pageable; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.*; -import org.springframework.web.bind.annotation.RequestMapping; -import org.springframework.web.bind.annotation.RestController; +import jakarta.validation.Valid; +import java.time.LocalDateTime; @RestController @RequiredArgsConstructor -@RequestMapping("/members/coupons") +@RequestMapping("/api/members/coupons") public class MemberCouponController { - private final CouponService couponService; + public static final String X_USER_ID = "X-USER-ID"; + private final MemberCouponService memberCouponService; + @PostMapping("/{couponId}") + public ResponseEntity issueCoupon( + @RequestHeader(X_USER_ID) Long customerId, + @PathVariable Long couponId + ) { + MemberCouponResponse response = memberCouponService.issueCoupon(customerId, couponId); + return ResponseEntity.status(HttpStatus.CREATED).body(response); + } + + /** + * 회원 쿠폰 사용 + * + * @param memberCouponId 회원 쿠폰 ID + * @return MemberCouponResponse + */ + @PutMapping("/{memberCouponId}/use") + public ResponseEntity useCoupon(@PathVariable Long memberCouponId) { + MemberCouponResponse response = memberCouponService.useCoupon(memberCouponId); + return ResponseEntity.ok(response); + } + + /** + * 회원의 쿠폰 조회 + * + * @param memberId 회원 ID + * @param pageable Pageable 객체 + * @return Page + */ + @GetMapping("/{memberId}") + public ResponseEntity> getUserCoupons( + @PathVariable Long memberId, + Pageable pageable + ) { + Page responses = memberCouponService.getUserCoupons(pageable, memberId); + return ResponseEntity.ok(responses); + } } diff --git a/src/main/java/com/nhnacademy/heukbaekbookshop/couponset/coupon/domain/CouponHistory.java b/src/main/java/com/nhnacademy/heukbaekbookshop/couponset/coupon/domain/CouponHistory.java index 1e853ec2..17d02d81 100644 --- a/src/main/java/com/nhnacademy/heukbaekbookshop/couponset/coupon/domain/CouponHistory.java +++ b/src/main/java/com/nhnacademy/heukbaekbookshop/couponset/coupon/domain/CouponHistory.java @@ -12,7 +12,6 @@ @Entity @Getter -@Setter @NoArgsConstructor @AllArgsConstructor @Table(name = "coupons_histories") diff --git a/src/main/java/com/nhnacademy/heukbaekbookshop/couponset/coupon/domain/MemberCoupon.java b/src/main/java/com/nhnacademy/heukbaekbookshop/couponset/coupon/domain/MemberCoupon.java index b7444ad4..d2e9715b 100644 --- a/src/main/java/com/nhnacademy/heukbaekbookshop/couponset/coupon/domain/MemberCoupon.java +++ b/src/main/java/com/nhnacademy/heukbaekbookshop/couponset/coupon/domain/MemberCoupon.java @@ -3,16 +3,12 @@ import com.nhnacademy.heukbaekbookshop.memberset.member.domain.Member; import jakarta.persistence.*; import jakarta.validation.constraints.NotNull; -import lombok.AllArgsConstructor; -import lombok.Getter; -import lombok.NoArgsConstructor; -import lombok.Setter; +import lombok.*; import java.time.LocalDateTime; @Entity @Getter -@Setter @NoArgsConstructor @AllArgsConstructor @Table(name = "members_coupons") @@ -37,10 +33,19 @@ public class MemberCoupon { @NotNull @Column(name = "coupon_created_at") - private LocalDateTime createdAt; + private LocalDateTime issuedAt; @NotNull - @Column(name = "coupon_expiration_at") + @Column(name = "coupon_expiration_date") private LocalDateTime expirationAt; + + @Builder + private MemberCoupon(Member member, Coupon coupon, LocalDateTime issuedAt, LocalDateTime expirationAt) { + this.member = member; + this.coupon = coupon; + this.issuedAt = issuedAt; + this.expirationAt = expirationAt; + this.isCouponUsed = false; + } } diff --git a/src/main/java/com/nhnacademy/heukbaekbookshop/couponset/coupon/dto/mapper/CouponHistoryMapper.java b/src/main/java/com/nhnacademy/heukbaekbookshop/couponset/coupon/dto/mapper/CouponHistoryMapper.java new file mode 100644 index 00000000..0bfb046b --- /dev/null +++ b/src/main/java/com/nhnacademy/heukbaekbookshop/couponset/coupon/dto/mapper/CouponHistoryMapper.java @@ -0,0 +1,18 @@ +package com.nhnacademy.heukbaekbookshop.couponset.coupon.dto.mapper; + +import com.nhnacademy.heukbaekbookshop.couponset.coupon.domain.CouponHistory; +import com.nhnacademy.heukbaekbookshop.couponset.coupon.dto.response.CouponHistoryResponse; + +public class CouponHistoryMapper { + public static CouponHistoryResponse toResponse(CouponHistory history) { + return new CouponHistoryResponse( + history.getId(), + history.getMemberCoupon().getId(), + history.getUsedAt(), + history.getOrderBook().getBookId(), + history.getOrderBook().getOrderId() + ); + } +} + + diff --git a/src/main/java/com/nhnacademy/heukbaekbookshop/couponset/coupon/dto/mapper/CouponMapper.java b/src/main/java/com/nhnacademy/heukbaekbookshop/couponset/coupon/dto/mapper/CouponMapper.java index 0cfda666..90573abd 100644 --- a/src/main/java/com/nhnacademy/heukbaekbookshop/couponset/coupon/dto/mapper/CouponMapper.java +++ b/src/main/java/com/nhnacademy/heukbaekbookshop/couponset/coupon/dto/mapper/CouponMapper.java @@ -2,17 +2,19 @@ import com.nhnacademy.heukbaekbookshop.book.domain.Book; import com.nhnacademy.heukbaekbookshop.category.domain.Category; -import com.nhnacademy.heukbaekbookshop.couponset.coupon.domain.BookCoupon; -import com.nhnacademy.heukbaekbookshop.couponset.coupon.domain.CategoryCoupon; -import com.nhnacademy.heukbaekbookshop.couponset.coupon.domain.Coupon; -import com.nhnacademy.heukbaekbookshop.couponset.coupon.domain.CouponStatus; +import com.nhnacademy.heukbaekbookshop.couponset.coupon.domain.*; import com.nhnacademy.heukbaekbookshop.couponset.coupon.dto.request.CouponRequest; +import com.nhnacademy.heukbaekbookshop.couponset.coupon.dto.response.CouponHistoryResponse; import com.nhnacademy.heukbaekbookshop.couponset.coupon.dto.response.CouponResponse; +import com.nhnacademy.heukbaekbookshop.couponset.coupon.dto.response.MemberCouponResponse; import com.nhnacademy.heukbaekbookshop.couponset.couponpolicy.domain.CouponPolicy; import com.nhnacademy.heukbaekbookshop.couponset.couponpolicy.dto.mapper.CouponPolicyMapper; +import com.nhnacademy.heukbaekbookshop.memberset.member.domain.Member; import org.springframework.data.domain.Page; import org.springframework.data.domain.Pageable; +import java.time.LocalDateTime; + public class CouponMapper { public static CouponResponse fromEntity(Coupon coupon) { @@ -65,4 +67,25 @@ public static CategoryCoupon toCategoryCouponEntity(CouponRequest couponRequest, category); } + public static MemberCoupon toMemberCouponEntity(Member member, Coupon coupon, int availableDate){ + return MemberCoupon.builder() + .member(member) + .coupon(coupon) + .issuedAt(LocalDateTime.now()) + .expirationAt(LocalDateTime.now().plusDays(availableDate)).build(); + } + + public static MemberCouponResponse fromMemberCouponEntity(MemberCoupon memberCoupon) { + return new MemberCouponResponse( + memberCoupon.getId(), + memberCoupon.getCoupon().getId(), + memberCoupon.isCouponUsed(), + memberCoupon.getIssuedAt(), + memberCoupon.getExpirationAt() + ); + } + + public static Page fromMemberCouponPageableEntity(Page memberCoupons) { + return memberCoupons.map(CouponMapper::fromMemberCouponEntity); + } } diff --git a/src/main/java/com/nhnacademy/heukbaekbookshop/couponset/coupon/dto/response/CouponHistoryResponse.java b/src/main/java/com/nhnacademy/heukbaekbookshop/couponset/coupon/dto/response/CouponHistoryResponse.java index e5c72b68..d20b12a1 100644 --- a/src/main/java/com/nhnacademy/heukbaekbookshop/couponset/coupon/dto/response/CouponHistoryResponse.java +++ b/src/main/java/com/nhnacademy/heukbaekbookshop/couponset/coupon/dto/response/CouponHistoryResponse.java @@ -1,21 +1,12 @@ package com.nhnacademy.heukbaekbookshop.couponset.coupon.dto.response; -import com.nhnacademy.heukbaekbookshop.couponset.coupon.domain.CouponHistory; - import java.time.LocalDateTime; public record CouponHistoryResponse( - Long historyId, - LocalDateTime couponUsedAt, - Long bookId, - Long orderId -) { - public static CouponHistoryResponse from(CouponHistory usageHistory) { - return new CouponHistoryResponse( - usageHistory.getId(), - usageHistory.getUsedAt(), - usageHistory.getOrderBook() != null ? usageHistory.getOrderBook().getBook().getId() : null, - usageHistory.getOrderBook() != null ? usageHistory.getOrderBook().getOrder().getId() : null - ); - } -} + Long couponHistoryId, // 쿠폰 사용 내역 ID + Long memberCouponId, // 회원 쿠폰 ID + LocalDateTime usedAt, // 쿠폰 사용일 + Long bookId, // 사용한 도서 번호 + Long orderId // 사용한 주문 번호 +) {} + diff --git a/src/main/java/com/nhnacademy/heukbaekbookshop/couponset/coupon/dto/response/MemberCouponResponse.java b/src/main/java/com/nhnacademy/heukbaekbookshop/couponset/coupon/dto/response/MemberCouponResponse.java index c98d80ef..a209b5bd 100644 --- a/src/main/java/com/nhnacademy/heukbaekbookshop/couponset/coupon/dto/response/MemberCouponResponse.java +++ b/src/main/java/com/nhnacademy/heukbaekbookshop/couponset/coupon/dto/response/MemberCouponResponse.java @@ -1,5 +1,6 @@ package com.nhnacademy.heukbaekbookshop.couponset.coupon.dto.response; +import com.nhnacademy.heukbaekbookshop.couponset.coupon.domain.Coupon; import com.nhnacademy.heukbaekbookshop.couponset.coupon.domain.MemberCoupon; import java.time.LocalDateTime; @@ -7,8 +8,9 @@ public record MemberCouponResponse( Long memberCouponId, Long couponId, - LocalDateTime couponCreatedAt, - LocalDateTime couponExpirationDate, - Boolean isCouponUsed + Boolean isCouponUsed, + LocalDateTime couponIssuedAt, + LocalDateTime couponExpirationDate + ) { } diff --git a/src/main/java/com/nhnacademy/heukbaekbookshop/couponset/coupon/exception/MemberCouponNotFoundException.java b/src/main/java/com/nhnacademy/heukbaekbookshop/couponset/coupon/exception/MemberCouponNotFoundException.java new file mode 100644 index 00000000..a4af5f75 --- /dev/null +++ b/src/main/java/com/nhnacademy/heukbaekbookshop/couponset/coupon/exception/MemberCouponNotFoundException.java @@ -0,0 +1,8 @@ +package com.nhnacademy.heukbaekbookshop.couponset.coupon.exception; + +public class MemberCouponNotFoundException extends RuntimeException { + public MemberCouponNotFoundException(String message) { + super(message); + } +} + diff --git a/src/main/java/com/nhnacademy/heukbaekbookshop/couponset/coupon/repository/CouponHistoryRepository.java b/src/main/java/com/nhnacademy/heukbaekbookshop/couponset/coupon/repository/CouponHistoryRepository.java index 37094800..2d9c7682 100644 --- a/src/main/java/com/nhnacademy/heukbaekbookshop/couponset/coupon/repository/CouponHistoryRepository.java +++ b/src/main/java/com/nhnacademy/heukbaekbookshop/couponset/coupon/repository/CouponHistoryRepository.java @@ -6,7 +6,8 @@ import org.springframework.data.jpa.repository.JpaRepository; public interface CouponHistoryRepository extends JpaRepository { - Page findByMemberCouponId(Pageable pageable, Long memberCouponId); + // 사용자 ID를 기준으로 쿠폰 사용 내역 조회 + Page findByMemberCoupon_Member_Id(Pageable pageable, Long memberId); } diff --git a/src/main/java/com/nhnacademy/heukbaekbookshop/couponset/coupon/service/CouponHistoryService.java b/src/main/java/com/nhnacademy/heukbaekbookshop/couponset/coupon/service/CouponHistoryService.java new file mode 100644 index 00000000..dedf3274 --- /dev/null +++ b/src/main/java/com/nhnacademy/heukbaekbookshop/couponset/coupon/service/CouponHistoryService.java @@ -0,0 +1,9 @@ +package com.nhnacademy.heukbaekbookshop.couponset.coupon.service; + +import com.nhnacademy.heukbaekbookshop.couponset.coupon.dto.response.CouponHistoryResponse; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.Pageable; + +public interface CouponHistoryService { + Page getCouponHistoryByCustomerId(Long memberId, Pageable pageable); +} diff --git a/src/main/java/com/nhnacademy/heukbaekbookshop/couponset/coupon/service/MemberCouponService.java b/src/main/java/com/nhnacademy/heukbaekbookshop/couponset/coupon/service/MemberCouponService.java index ccceb34e..ef01b03b 100644 --- a/src/main/java/com/nhnacademy/heukbaekbookshop/couponset/coupon/service/MemberCouponService.java +++ b/src/main/java/com/nhnacademy/heukbaekbookshop/couponset/coupon/service/MemberCouponService.java @@ -5,8 +5,11 @@ import org.springframework.data.domain.Page; import org.springframework.data.domain.Pageable; +import java.time.LocalDateTime; + public interface MemberCouponService { + MemberCouponResponse issueCoupon(Long memberId, Long couponId); + MemberCouponResponse useCoupon(Long memberCouponId); // 쿠폰 조회 관련 메서드 Page getUserCoupons(Pageable pageable, Long memberId); - Page getCouponHistory(Pageable pageable, Long memberCouponId); } diff --git a/src/main/java/com/nhnacademy/heukbaekbookshop/couponset/coupon/service/impl/CouponHistoryServiceImpl.java b/src/main/java/com/nhnacademy/heukbaekbookshop/couponset/coupon/service/impl/CouponHistoryServiceImpl.java new file mode 100644 index 00000000..9d7155e8 --- /dev/null +++ b/src/main/java/com/nhnacademy/heukbaekbookshop/couponset/coupon/service/impl/CouponHistoryServiceImpl.java @@ -0,0 +1,25 @@ +package com.nhnacademy.heukbaekbookshop.couponset.coupon.service.impl; + +import com.nhnacademy.heukbaekbookshop.couponset.coupon.dto.mapper.CouponHistoryMapper; +import com.nhnacademy.heukbaekbookshop.couponset.coupon.dto.response.CouponHistoryResponse; +import com.nhnacademy.heukbaekbookshop.couponset.coupon.repository.CouponHistoryRepository; +import com.nhnacademy.heukbaekbookshop.couponset.coupon.service.CouponHistoryService; +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 CouponHistoryServiceImpl implements CouponHistoryService { + private final CouponHistoryRepository couponHistoryRepository; + + @Override + @Transactional(readOnly = true) + public Page getCouponHistoryByCustomerId(Long memberId, Pageable pageable) { + // 사용자 ID 기준으로 쿠폰 사용 내역 조회 후 DTO로 변환 + return couponHistoryRepository.findByMemberCoupon_Member_Id(pageable, memberId) + .map(CouponHistoryMapper::toResponse); + } +} diff --git a/src/main/java/com/nhnacademy/heukbaekbookshop/couponset/coupon/service/impl/MemberCouponServiceImpl.java b/src/main/java/com/nhnacademy/heukbaekbookshop/couponset/coupon/service/impl/MemberCouponServiceImpl.java index 39d035c8..392a30d8 100644 --- a/src/main/java/com/nhnacademy/heukbaekbookshop/couponset/coupon/service/impl/MemberCouponServiceImpl.java +++ b/src/main/java/com/nhnacademy/heukbaekbookshop/couponset/coupon/service/impl/MemberCouponServiceImpl.java @@ -1,38 +1,85 @@ package com.nhnacademy.heukbaekbookshop.couponset.coupon.service.impl; +import com.nhnacademy.heukbaekbookshop.couponset.coupon.domain.Coupon; +import com.nhnacademy.heukbaekbookshop.couponset.coupon.domain.CouponHistory; +import com.nhnacademy.heukbaekbookshop.couponset.coupon.domain.MemberCoupon; +import com.nhnacademy.heukbaekbookshop.couponset.coupon.dto.mapper.CouponMapper; +import com.nhnacademy.heukbaekbookshop.couponset.coupon.dto.response.CouponHistoryResponse; +import com.nhnacademy.heukbaekbookshop.couponset.coupon.dto.response.MemberCouponResponse; +import com.nhnacademy.heukbaekbookshop.couponset.coupon.exception.CouponNotFoundException; +import com.nhnacademy.heukbaekbookshop.couponset.coupon.exception.MemberCouponNotFoundException; import com.nhnacademy.heukbaekbookshop.couponset.coupon.repository.CouponHistoryRepository; +import com.nhnacademy.heukbaekbookshop.couponset.coupon.repository.CouponRepository; import com.nhnacademy.heukbaekbookshop.couponset.coupon.repository.MemberCouponRepository; +import com.nhnacademy.heukbaekbookshop.couponset.coupon.service.MemberCouponService; +import com.nhnacademy.heukbaekbookshop.memberset.member.domain.Member; +import com.nhnacademy.heukbaekbookshop.memberset.member.exception.MemberNotFoundException; +import com.nhnacademy.heukbaekbookshop.memberset.member.repository.MemberRepository; +import com.nhnacademy.heukbaekbookshop.order.domain.OrderBook; import lombok.RequiredArgsConstructor; - +import org.springframework.data.domain.Page; +import org.springframework.data.domain.Pageable; import org.springframework.stereotype.Service; +import java.time.LocalDateTime; + @Service @RequiredArgsConstructor -public class MemberCouponServiceImpl { +public class MemberCouponServiceImpl implements MemberCouponService { private final MemberCouponRepository memberCouponRepository; private final CouponHistoryRepository couponHistoryRepository; + private final MemberRepository memberRepository; + private final CouponRepository couponRepository; + + @Override + public MemberCouponResponse issueCoupon(Long memberId, Long couponId) { + Member member = memberRepository.findById(memberId) + .orElseThrow(MemberNotFoundException::new); + + Coupon coupon = couponRepository.findById(couponId) + .orElseThrow(() -> new CouponNotFoundException("Coupon not found: " + couponId)); + + MemberCoupon memberCoupon = CouponMapper.toMemberCouponEntity(member, coupon, coupon.getAvailableDuration()); - // @Override -// public CouponInfoResponse getCouponDetails(Long couponId) { -// Coupon coupon = couponRepository.findById(couponId) -// .orElseThrow(() -> new RuntimeException("Coupon not found")); -// return CouponInfoResponse.from(coupon); -// } + return CouponMapper.fromMemberCouponEntity(memberCouponRepository.save(memberCoupon)); + } -// @Override -// public Page getUserCoupons(Pageable pageable, Long memberId) { -// Page memberCoupons = memberCouponRepository.findByMemberId(pageable, memberId); -// return memberCoupons.map(MemberCouponResponse::from); -// } + @Override + public MemberCouponResponse useCoupon(Long memberCouponId) { + MemberCoupon memberCoupon = memberCouponRepository.findById(memberCouponId) + .orElseThrow(() -> new MemberCouponNotFoundException("MemberCoupon not found: " + memberCouponId)); + if (memberCoupon.isCouponUsed()) { + throw new IllegalStateException("Coupon is already used."); + } -// -// -// @Override -// public Page getCouponHistory(Pageable pageable, Long memberCouponId) { -// Page usageHistory = couponHistoryRepository.findByMemberCouponId(pageable, memberCouponId); -// return usageHistory.map(CouponHistoryResponse::from); -// } + // 쿠폰 사용 내역 저장 + CouponHistory couponHistory = new CouponHistory( + null, + memberCoupon, + LocalDateTime.now(), + new OrderBook() + ); + couponHistoryRepository.save(couponHistory); + + MemberCoupon updatedCoupon = new MemberCoupon( + memberCoupon.getId(), + memberCoupon.getMember(), + memberCoupon.getCoupon(), + true, + memberCoupon.getIssuedAt(), + memberCoupon.getExpirationAt() + ); + + return CouponMapper.fromMemberCouponEntity(memberCouponRepository.save(updatedCoupon)); + } + + @Override + public Page getUserCoupons(Pageable pageable, Long memberId) { + Page memberCoupons = memberCouponRepository.findByMemberId(pageable, memberId); + return CouponMapper.fromMemberCouponPageableEntity(memberCoupons); + } } + diff --git a/src/main/java/com/nhnacademy/heukbaekbookshop/memberset/member/exception/MemberNotFoundException.java b/src/main/java/com/nhnacademy/heukbaekbookshop/memberset/member/exception/MemberNotFoundException.java index 2155706b..474c6a71 100644 --- a/src/main/java/com/nhnacademy/heukbaekbookshop/memberset/member/exception/MemberNotFoundException.java +++ b/src/main/java/com/nhnacademy/heukbaekbookshop/memberset/member/exception/MemberNotFoundException.java @@ -1,7 +1,10 @@ package com.nhnacademy.heukbaekbookshop.memberset.member.exception; +import lombok.NoArgsConstructor; + +@NoArgsConstructor public class MemberNotFoundException extends RuntimeException { -// public MemberNotFoundException(Long customerId) { -// super("member not found: " + customerId); -// } + public MemberNotFoundException(Long customerId) { + super("member not found: " + customerId); + } } From 25f2aa8a23a5a0a0add9c7fdc6daa9e2b3a33552 Mon Sep 17 00:00:00 2001 From: juhabae Date: Thu, 21 Nov 2024 23:35:45 +0900 Subject: [PATCH 2/3] =?UTF-8?q?[Feat]=20coupon=20history=20=EA=B5=AC?= =?UTF-8?q?=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../controller/CouponHistoryController.java | 31 ++++++++++---- .../controller/MemberCouponController.java | 13 +++++- .../coupon/domain/CouponHistory.java | 6 ++- .../couponset/coupon/domain/MemberCoupon.java | 8 ++++ .../dto/mapper/CouponHistoryMapper.java | 29 +++++++++---- .../coupon/dto/mapper/CouponMapper.java | 12 ++++++ .../dto/request/CouponHistoryRequest.java | 7 ++++ .../coupon/dto/request/UseCouponRequest.java | 4 ++ .../dto/response/CouponHistoryResponse.java | 13 +++--- .../coupon/service/CouponHistoryService.java | 3 ++ .../coupon/service/MemberCouponService.java | 2 +- .../impl/CouponHistoryServiceImpl.java | 31 +++++++++++++- .../service/impl/MemberCouponServiceImpl.java | 41 ++++++++----------- .../heukbaekbookshop/order/domain/Order.java | 2 +- 14 files changed, 153 insertions(+), 49 deletions(-) create mode 100644 src/main/java/com/nhnacademy/heukbaekbookshop/couponset/coupon/dto/request/CouponHistoryRequest.java create mode 100644 src/main/java/com/nhnacademy/heukbaekbookshop/couponset/coupon/dto/request/UseCouponRequest.java diff --git a/src/main/java/com/nhnacademy/heukbaekbookshop/couponset/coupon/controller/CouponHistoryController.java b/src/main/java/com/nhnacademy/heukbaekbookshop/couponset/coupon/controller/CouponHistoryController.java index 74c58525..c35aeb08 100644 --- a/src/main/java/com/nhnacademy/heukbaekbookshop/couponset/coupon/controller/CouponHistoryController.java +++ b/src/main/java/com/nhnacademy/heukbaekbookshop/couponset/coupon/controller/CouponHistoryController.java @@ -1,31 +1,46 @@ package com.nhnacademy.heukbaekbookshop.couponset.coupon.controller; +import com.nhnacademy.heukbaekbookshop.couponset.coupon.dto.request.CouponHistoryRequest; import com.nhnacademy.heukbaekbookshop.couponset.coupon.dto.response.CouponHistoryResponse; import com.nhnacademy.heukbaekbookshop.couponset.coupon.service.CouponHistoryService; import lombok.RequiredArgsConstructor; import org.springframework.data.domain.Page; import org.springframework.data.domain.Pageable; +import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.*; @RestController -@RequestMapping("/api/coupons/histories") @RequiredArgsConstructor +@RequestMapping("/api/coupons/histories") public class CouponHistoryController { private final CouponHistoryService couponHistoryService; /** * 사용자 쿠폰 사용 내역 조회 - * @param memberId 사용자 ID - * @param pageable 페이징 객체 - * @return 쿠폰 사용 내역 + * + * @param memberId 회원 ID + * @param pageable 페이징 정보 + * @return 쿠폰 사용 내역 목록 */ @GetMapping("/members/{memberId}") public ResponseEntity> getCouponHistoriesByUser( @PathVariable Long memberId, - Pageable pageable) { - Page responses = couponHistoryService.getCouponHistoryByCustomerId(memberId, pageable); - return ResponseEntity.ok(responses); + Pageable pageable + ) { + Page histories = couponHistoryService.getCouponHistoryByCustomerId(memberId, pageable); + return ResponseEntity.ok(histories); } -} + /** + * 쿠폰 사용 기록 생성 + * + * @param couponHistoryRequest 쿠폰 사용 요청 DTO + * @return 성공 여부 + */ + @PostMapping + public ResponseEntity createCouponHistory(@RequestBody CouponHistoryRequest couponHistoryRequest) { + couponHistoryService.createCouponHistory(couponHistoryRequest); + return ResponseEntity.status(HttpStatus.CREATED).build(); + } +} diff --git a/src/main/java/com/nhnacademy/heukbaekbookshop/couponset/coupon/controller/MemberCouponController.java b/src/main/java/com/nhnacademy/heukbaekbookshop/couponset/coupon/controller/MemberCouponController.java index 22d53d12..df58c98b 100644 --- a/src/main/java/com/nhnacademy/heukbaekbookshop/couponset/coupon/controller/MemberCouponController.java +++ b/src/main/java/com/nhnacademy/heukbaekbookshop/couponset/coupon/controller/MemberCouponController.java @@ -1,5 +1,6 @@ package com.nhnacademy.heukbaekbookshop.couponset.coupon.controller; +import com.nhnacademy.heukbaekbookshop.couponset.coupon.dto.request.UseCouponRequest; import com.nhnacademy.heukbaekbookshop.couponset.coupon.dto.response.CouponHistoryResponse; import com.nhnacademy.heukbaekbookshop.couponset.coupon.dto.response.MemberCouponResponse; import com.nhnacademy.heukbaekbookshop.couponset.coupon.service.MemberCouponService; @@ -35,11 +36,19 @@ public ResponseEntity issueCoupon( * 회원 쿠폰 사용 * * @param memberCouponId 회원 쿠폰 ID + * @param useRequest 요청 본문에 포함된 주문 ID와 도서 ID * @return MemberCouponResponse */ @PutMapping("/{memberCouponId}/use") - public ResponseEntity useCoupon(@PathVariable Long memberCouponId) { - MemberCouponResponse response = memberCouponService.useCoupon(memberCouponId); + public ResponseEntity useCoupon( + @PathVariable Long memberCouponId, + @RequestBody UseCouponRequest useRequest) { + + MemberCouponResponse response = memberCouponService.useCoupon( + memberCouponId, + useRequest.orderId(), + useRequest.bookId() + ); return ResponseEntity.ok(response); } diff --git a/src/main/java/com/nhnacademy/heukbaekbookshop/couponset/coupon/domain/CouponHistory.java b/src/main/java/com/nhnacademy/heukbaekbookshop/couponset/coupon/domain/CouponHistory.java index 17d02d81..b9a06cb6 100644 --- a/src/main/java/com/nhnacademy/heukbaekbookshop/couponset/coupon/domain/CouponHistory.java +++ b/src/main/java/com/nhnacademy/heukbaekbookshop/couponset/coupon/domain/CouponHistory.java @@ -6,7 +6,6 @@ import lombok.AllArgsConstructor; import lombok.Getter; import lombok.NoArgsConstructor; -import lombok.Setter; import java.time.LocalDateTime; @@ -37,4 +36,9 @@ public class CouponHistory { }) private OrderBook orderBook; + @ManyToOne + @JoinColumn(name = "coupon_id", referencedColumnName = "coupon_id") + private Coupon coupon; } + + diff --git a/src/main/java/com/nhnacademy/heukbaekbookshop/couponset/coupon/domain/MemberCoupon.java b/src/main/java/com/nhnacademy/heukbaekbookshop/couponset/coupon/domain/MemberCoupon.java index d2e9715b..c5c0aaad 100644 --- a/src/main/java/com/nhnacademy/heukbaekbookshop/couponset/coupon/domain/MemberCoupon.java +++ b/src/main/java/com/nhnacademy/heukbaekbookshop/couponset/coupon/domain/MemberCoupon.java @@ -48,4 +48,12 @@ private MemberCoupon(Member member, Coupon coupon, LocalDateTime issuedAt, Local this.expirationAt = expirationAt; this.isCouponUsed = false; } + + public void markAsUsed() { + if (this.isCouponUsed) { + throw new IllegalStateException("쿠폰이 이미 사용되었습니다"); + } + this.isCouponUsed = true; + } + } diff --git a/src/main/java/com/nhnacademy/heukbaekbookshop/couponset/coupon/dto/mapper/CouponHistoryMapper.java b/src/main/java/com/nhnacademy/heukbaekbookshop/couponset/coupon/dto/mapper/CouponHistoryMapper.java index 0bfb046b..a874b22c 100644 --- a/src/main/java/com/nhnacademy/heukbaekbookshop/couponset/coupon/dto/mapper/CouponHistoryMapper.java +++ b/src/main/java/com/nhnacademy/heukbaekbookshop/couponset/coupon/dto/mapper/CouponHistoryMapper.java @@ -1,18 +1,33 @@ package com.nhnacademy.heukbaekbookshop.couponset.coupon.dto.mapper; import com.nhnacademy.heukbaekbookshop.couponset.coupon.domain.CouponHistory; +import com.nhnacademy.heukbaekbookshop.couponset.coupon.domain.MemberCoupon; import com.nhnacademy.heukbaekbookshop.couponset.coupon.dto.response.CouponHistoryResponse; +import com.nhnacademy.heukbaekbookshop.order.domain.OrderBook; + +import java.time.LocalDateTime; public class CouponHistoryMapper { - public static CouponHistoryResponse toResponse(CouponHistory history) { + public static CouponHistoryResponse toResponse(CouponHistory couponHistory) { return new CouponHistoryResponse( - history.getId(), - history.getMemberCoupon().getId(), - history.getUsedAt(), - history.getOrderBook().getBookId(), - history.getOrderBook().getOrderId() + couponHistory.getId(), + couponHistory.getMemberCoupon().getId(), + couponHistory.getMemberCoupon().getMember().getId(), + couponHistory.getMemberCoupon().getCoupon().getId(), + couponHistory.getUsedAt(), + couponHistory.getOrderBook().getBook().getId(), + couponHistory.getOrderBook().getOrder().getId() ); } -} + public static CouponHistory toCouponHistoryEntity(MemberCoupon memberCoupon, OrderBook orderBook) { + return new CouponHistory( + null, + memberCoupon, + LocalDateTime.now(), + orderBook, + memberCoupon.getCoupon() + ); + } +} diff --git a/src/main/java/com/nhnacademy/heukbaekbookshop/couponset/coupon/dto/mapper/CouponMapper.java b/src/main/java/com/nhnacademy/heukbaekbookshop/couponset/coupon/dto/mapper/CouponMapper.java index 90573abd..36f7c247 100644 --- a/src/main/java/com/nhnacademy/heukbaekbookshop/couponset/coupon/dto/mapper/CouponMapper.java +++ b/src/main/java/com/nhnacademy/heukbaekbookshop/couponset/coupon/dto/mapper/CouponMapper.java @@ -10,6 +10,7 @@ import com.nhnacademy.heukbaekbookshop.couponset.couponpolicy.domain.CouponPolicy; import com.nhnacademy.heukbaekbookshop.couponset.couponpolicy.dto.mapper.CouponPolicyMapper; import com.nhnacademy.heukbaekbookshop.memberset.member.domain.Member; +import com.nhnacademy.heukbaekbookshop.order.domain.OrderBook; import org.springframework.data.domain.Page; import org.springframework.data.domain.Pageable; @@ -85,6 +86,17 @@ public static MemberCouponResponse fromMemberCouponEntity(MemberCoupon memberCou ); } + public static CouponHistory toCouponHistoryEntity(MemberCoupon memberCoupon, OrderBook orderBook) { + return new CouponHistory( + null, + memberCoupon, + LocalDateTime.now(), + orderBook, + memberCoupon.getCoupon() + ); + } + + public static Page fromMemberCouponPageableEntity(Page memberCoupons) { return memberCoupons.map(CouponMapper::fromMemberCouponEntity); } diff --git a/src/main/java/com/nhnacademy/heukbaekbookshop/couponset/coupon/dto/request/CouponHistoryRequest.java b/src/main/java/com/nhnacademy/heukbaekbookshop/couponset/coupon/dto/request/CouponHistoryRequest.java new file mode 100644 index 00000000..970f90bc --- /dev/null +++ b/src/main/java/com/nhnacademy/heukbaekbookshop/couponset/coupon/dto/request/CouponHistoryRequest.java @@ -0,0 +1,7 @@ +package com.nhnacademy.heukbaekbookshop.couponset.coupon.dto.request; + +public record CouponHistoryRequest( + Long memberCouponId, + Long orderId, + Long bookId +) {} diff --git a/src/main/java/com/nhnacademy/heukbaekbookshop/couponset/coupon/dto/request/UseCouponRequest.java b/src/main/java/com/nhnacademy/heukbaekbookshop/couponset/coupon/dto/request/UseCouponRequest.java new file mode 100644 index 00000000..8247dddb --- /dev/null +++ b/src/main/java/com/nhnacademy/heukbaekbookshop/couponset/coupon/dto/request/UseCouponRequest.java @@ -0,0 +1,4 @@ +package com.nhnacademy.heukbaekbookshop.couponset.coupon.dto.request; + +public record UseCouponRequest(Long orderId, Long bookId) { +} diff --git a/src/main/java/com/nhnacademy/heukbaekbookshop/couponset/coupon/dto/response/CouponHistoryResponse.java b/src/main/java/com/nhnacademy/heukbaekbookshop/couponset/coupon/dto/response/CouponHistoryResponse.java index d20b12a1..0eb271c1 100644 --- a/src/main/java/com/nhnacademy/heukbaekbookshop/couponset/coupon/dto/response/CouponHistoryResponse.java +++ b/src/main/java/com/nhnacademy/heukbaekbookshop/couponset/coupon/dto/response/CouponHistoryResponse.java @@ -3,10 +3,13 @@ import java.time.LocalDateTime; public record CouponHistoryResponse( - Long couponHistoryId, // 쿠폰 사용 내역 ID - Long memberCouponId, // 회원 쿠폰 ID - LocalDateTime usedAt, // 쿠폰 사용일 - Long bookId, // 사용한 도서 번호 - Long orderId // 사용한 주문 번호 + Long couponHistoryId, + Long memberCouponId, + Long memberId, + Long couponId, + LocalDateTime usedAt, + Long bookId, + Long orderId ) {} + diff --git a/src/main/java/com/nhnacademy/heukbaekbookshop/couponset/coupon/service/CouponHistoryService.java b/src/main/java/com/nhnacademy/heukbaekbookshop/couponset/coupon/service/CouponHistoryService.java index dedf3274..0bae1d62 100644 --- a/src/main/java/com/nhnacademy/heukbaekbookshop/couponset/coupon/service/CouponHistoryService.java +++ b/src/main/java/com/nhnacademy/heukbaekbookshop/couponset/coupon/service/CouponHistoryService.java @@ -1,9 +1,12 @@ package com.nhnacademy.heukbaekbookshop.couponset.coupon.service; +import com.nhnacademy.heukbaekbookshop.couponset.coupon.domain.MemberCoupon; +import com.nhnacademy.heukbaekbookshop.couponset.coupon.dto.request.CouponHistoryRequest; import com.nhnacademy.heukbaekbookshop.couponset.coupon.dto.response.CouponHistoryResponse; import org.springframework.data.domain.Page; import org.springframework.data.domain.Pageable; public interface CouponHistoryService { + void createCouponHistory(CouponHistoryRequest couponHistoryRequest); Page getCouponHistoryByCustomerId(Long memberId, Pageable pageable); } diff --git a/src/main/java/com/nhnacademy/heukbaekbookshop/couponset/coupon/service/MemberCouponService.java b/src/main/java/com/nhnacademy/heukbaekbookshop/couponset/coupon/service/MemberCouponService.java index ef01b03b..89c111a1 100644 --- a/src/main/java/com/nhnacademy/heukbaekbookshop/couponset/coupon/service/MemberCouponService.java +++ b/src/main/java/com/nhnacademy/heukbaekbookshop/couponset/coupon/service/MemberCouponService.java @@ -9,7 +9,7 @@ public interface MemberCouponService { MemberCouponResponse issueCoupon(Long memberId, Long couponId); - MemberCouponResponse useCoupon(Long memberCouponId); + MemberCouponResponse useCoupon(Long memberCouponId, Long orderId, Long bookId); // 쿠폰 조회 관련 메서드 Page getUserCoupons(Pageable pageable, Long memberId); } diff --git a/src/main/java/com/nhnacademy/heukbaekbookshop/couponset/coupon/service/impl/CouponHistoryServiceImpl.java b/src/main/java/com/nhnacademy/heukbaekbookshop/couponset/coupon/service/impl/CouponHistoryServiceImpl.java index 9d7155e8..dac9cda1 100644 --- a/src/main/java/com/nhnacademy/heukbaekbookshop/couponset/coupon/service/impl/CouponHistoryServiceImpl.java +++ b/src/main/java/com/nhnacademy/heukbaekbookshop/couponset/coupon/service/impl/CouponHistoryServiceImpl.java @@ -1,24 +1,53 @@ package com.nhnacademy.heukbaekbookshop.couponset.coupon.service.impl; +import com.nhnacademy.heukbaekbookshop.couponset.coupon.domain.CouponHistory; +import com.nhnacademy.heukbaekbookshop.couponset.coupon.domain.MemberCoupon; import com.nhnacademy.heukbaekbookshop.couponset.coupon.dto.mapper.CouponHistoryMapper; +import com.nhnacademy.heukbaekbookshop.couponset.coupon.dto.request.CouponHistoryRequest; import com.nhnacademy.heukbaekbookshop.couponset.coupon.dto.response.CouponHistoryResponse; import com.nhnacademy.heukbaekbookshop.couponset.coupon.repository.CouponHistoryRepository; +import com.nhnacademy.heukbaekbookshop.couponset.coupon.repository.MemberCouponRepository; import com.nhnacademy.heukbaekbookshop.couponset.coupon.service.CouponHistoryService; +import com.nhnacademy.heukbaekbookshop.order.domain.OrderBook; +import com.nhnacademy.heukbaekbookshop.order.repository.OrderBookRepository; 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; +import java.time.LocalDateTime; + @Service @RequiredArgsConstructor public class CouponHistoryServiceImpl implements CouponHistoryService { private final CouponHistoryRepository couponHistoryRepository; + private final OrderBookRepository orderBookRepository; + private final MemberCouponRepository memberCouponRepository; + + @Override + @Transactional + public void createCouponHistory(CouponHistoryRequest couponHistoryRequest) { + MemberCoupon memberCoupon = memberCouponRepository.findById(couponHistoryRequest.memberCouponId()) + .orElseThrow(() -> new IllegalStateException("해당 ID의 회원 쿠폰을 찾을 수 없습니다 : " + + couponHistoryRequest.memberCouponId())); + + OrderBook orderBook = orderBookRepository.findByOrderIdAndBookId( + couponHistoryRequest.orderId(), couponHistoryRequest.bookId()); + if (orderBook == null) { + throw new IllegalStateException("해당 주문 ID에 대한 주문 도서를 찾을 수 없습니다 : " + + couponHistoryRequest.orderId() + " 해당 도서 ID에 대한 주문 도서를 찾을 수 없습니다 : " + couponHistoryRequest.bookId()); + } + + CouponHistory couponHistory = CouponHistoryMapper.toCouponHistoryEntity(memberCoupon, orderBook); + + couponHistoryRepository.save(couponHistory); + } + @Override @Transactional(readOnly = true) public Page getCouponHistoryByCustomerId(Long memberId, Pageable pageable) { - // 사용자 ID 기준으로 쿠폰 사용 내역 조회 후 DTO로 변환 return couponHistoryRepository.findByMemberCoupon_Member_Id(pageable, memberId) .map(CouponHistoryMapper::toResponse); } diff --git a/src/main/java/com/nhnacademy/heukbaekbookshop/couponset/coupon/service/impl/MemberCouponServiceImpl.java b/src/main/java/com/nhnacademy/heukbaekbookshop/couponset/coupon/service/impl/MemberCouponServiceImpl.java index 392a30d8..5c68e86b 100644 --- a/src/main/java/com/nhnacademy/heukbaekbookshop/couponset/coupon/service/impl/MemberCouponServiceImpl.java +++ b/src/main/java/com/nhnacademy/heukbaekbookshop/couponset/coupon/service/impl/MemberCouponServiceImpl.java @@ -4,18 +4,21 @@ import com.nhnacademy.heukbaekbookshop.couponset.coupon.domain.CouponHistory; import com.nhnacademy.heukbaekbookshop.couponset.coupon.domain.MemberCoupon; import com.nhnacademy.heukbaekbookshop.couponset.coupon.dto.mapper.CouponMapper; -import com.nhnacademy.heukbaekbookshop.couponset.coupon.dto.response.CouponHistoryResponse; +import com.nhnacademy.heukbaekbookshop.couponset.coupon.dto.request.CouponHistoryRequest; import com.nhnacademy.heukbaekbookshop.couponset.coupon.dto.response.MemberCouponResponse; import com.nhnacademy.heukbaekbookshop.couponset.coupon.exception.CouponNotFoundException; import com.nhnacademy.heukbaekbookshop.couponset.coupon.exception.MemberCouponNotFoundException; import com.nhnacademy.heukbaekbookshop.couponset.coupon.repository.CouponHistoryRepository; import com.nhnacademy.heukbaekbookshop.couponset.coupon.repository.CouponRepository; import com.nhnacademy.heukbaekbookshop.couponset.coupon.repository.MemberCouponRepository; +import com.nhnacademy.heukbaekbookshop.couponset.coupon.service.CouponHistoryService; import com.nhnacademy.heukbaekbookshop.couponset.coupon.service.MemberCouponService; import com.nhnacademy.heukbaekbookshop.memberset.member.domain.Member; import com.nhnacademy.heukbaekbookshop.memberset.member.exception.MemberNotFoundException; import com.nhnacademy.heukbaekbookshop.memberset.member.repository.MemberRepository; import com.nhnacademy.heukbaekbookshop.order.domain.OrderBook; +import com.nhnacademy.heukbaekbookshop.order.repository.OrderBookRepository; +import jakarta.transaction.Transactional; import lombok.RequiredArgsConstructor; import org.springframework.data.domain.Page; import org.springframework.data.domain.Pageable; @@ -28,17 +31,19 @@ public class MemberCouponServiceImpl implements MemberCouponService { private final MemberCouponRepository memberCouponRepository; - private final CouponHistoryRepository couponHistoryRepository; private final MemberRepository memberRepository; private final CouponRepository couponRepository; + private final CouponHistoryRepository couponHistoryRepository; + private final OrderBookRepository orderBookRepository; @Override + @Transactional public MemberCouponResponse issueCoupon(Long memberId, Long couponId) { Member member = memberRepository.findById(memberId) .orElseThrow(MemberNotFoundException::new); Coupon coupon = couponRepository.findById(couponId) - .orElseThrow(() -> new CouponNotFoundException("Coupon not found: " + couponId)); + .orElseThrow(() -> new CouponNotFoundException("해당 ID의 쿠폰을 찾을 수 없습니다: " + couponId)); MemberCoupon memberCoupon = CouponMapper.toMemberCouponEntity(member, coupon, coupon.getAvailableDuration()); @@ -46,34 +51,24 @@ public MemberCouponResponse issueCoupon(Long memberId, Long couponId) { } @Override - public MemberCouponResponse useCoupon(Long memberCouponId) { + @Transactional + public MemberCouponResponse useCoupon(Long memberCouponId, Long orderId, Long bookId) { MemberCoupon memberCoupon = memberCouponRepository.findById(memberCouponId) - .orElseThrow(() -> new MemberCouponNotFoundException("MemberCoupon not found: " + memberCouponId)); + .orElseThrow(() -> new MemberCouponNotFoundException("해당 ID의 회원 쿠폰을 찾을 수 없습니다: " + memberCouponId)); if (memberCoupon.isCouponUsed()) { - throw new IllegalStateException("Coupon is already used."); + throw new IllegalStateException("쿠폰이 이미 사용되었습니다."); } - // 쿠폰 사용 내역 저장 - CouponHistory couponHistory = new CouponHistory( - null, - memberCoupon, - LocalDateTime.now(), - new OrderBook() - ); + OrderBook orderBook = orderBookRepository.findByOrderIdAndBookId(orderId, bookId); - couponHistoryRepository.save(couponHistory); + memberCoupon.markAsUsed(); + memberCouponRepository.save(memberCoupon); - MemberCoupon updatedCoupon = new MemberCoupon( - memberCoupon.getId(), - memberCoupon.getMember(), - memberCoupon.getCoupon(), - true, - memberCoupon.getIssuedAt(), - memberCoupon.getExpirationAt() - ); + CouponHistory couponHistory = CouponMapper.toCouponHistoryEntity(memberCoupon, orderBook); + couponHistoryRepository.save(couponHistory); - return CouponMapper.fromMemberCouponEntity(memberCouponRepository.save(updatedCoupon)); + return CouponMapper.fromMemberCouponEntity(memberCoupon); } @Override diff --git a/src/main/java/com/nhnacademy/heukbaekbookshop/order/domain/Order.java b/src/main/java/com/nhnacademy/heukbaekbookshop/order/domain/Order.java index b276cb8f..5d7a57f3 100644 --- a/src/main/java/com/nhnacademy/heukbaekbookshop/order/domain/Order.java +++ b/src/main/java/com/nhnacademy/heukbaekbookshop/order/domain/Order.java @@ -45,7 +45,7 @@ public class Order { private BigDecimal totalPrice; @NotNull - @Column(name = "order_cretaed_at") + @Column(name = "order_created_at") private LocalDateTime createdAt; @NotNull From 530aa0603ca3c9f98ba71396fef11ff3fd633ff4 Mon Sep 17 00:00:00 2001 From: juhabae Date: Mon, 25 Nov 2024 15:02:48 +0900 Subject: [PATCH 3/3] =?UTF-8?q?[Feat]membercoupon,=20couponhistory=20crud?= =?UTF-8?q?=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../coupon/CouponServiceImplTest.java | 103 ------------------ 1 file changed, 103 deletions(-) delete mode 100644 src/test/java/com/nhnacademy/heukbaekbookshop/coupon/CouponServiceImplTest.java diff --git a/src/test/java/com/nhnacademy/heukbaekbookshop/coupon/CouponServiceImplTest.java b/src/test/java/com/nhnacademy/heukbaekbookshop/coupon/CouponServiceImplTest.java deleted file mode 100644 index 6d7fffe0..00000000 --- a/src/test/java/com/nhnacademy/heukbaekbookshop/coupon/CouponServiceImplTest.java +++ /dev/null @@ -1,103 +0,0 @@ -//package com.nhnacademy.heukbaekbookshop.coupon; -// -//import com.nhnacademy.heukbaekbookshop.coupon.domain.CategoryCoupon; -//import com.nhnacademy.heukbaekbookshop.coupon.domain.Coupon; -//import com.nhnacademy.heukbaekbookshop.coupon.domain.CouponHistory; -//import com.nhnacademy.heukbaekbookshop.coupon.domain.MemberCoupon; -//import com.nhnacademy.heukbaekbookshop.coupon.dto.response.CouponHistoryResponse; -//import com.nhnacademy.heukbaekbookshop.coupon.dto.response.CouponInfoResponse; -//import com.nhnacademy.heukbaekbookshop.coupon.dto.response.MemberCouponResponse; -//import com.nhnacademy.heukbaekbookshop.coupon.repository.*; -//import com.nhnacademy.heukbaekbookshop.coupon.service.CouponService; -//import com.nhnacademy.heukbaekbookshop.coupon.service.CouponServiceImpl; -//import org.junit.jupiter.api.BeforeEach; -//import org.junit.jupiter.api.Test; -//import org.mockito.InjectMocks; -//import org.mockito.Mock; -//import org.mockito.MockitoAnnotations; -//import org.springframework.data.domain.Page; -//import org.springframework.data.domain.PageImpl; -//import org.springframework.data.domain.PageRequest; -//import java.util.Collections; -//import java.util.Optional; -//import static org.assertj.core.api.Assertions.assertThat; -//import static org.mockito.BDDMockito.given; -// -//class CouponServiceImplTest { -// -// @Mock -// private CouponRepository couponRepository; -// -// @Mock -// private MemberCouponRepository memberCouponRepository; -// -// @Mock -// private CouponHistoryRepository couponHistoryRepository; -// -// @Mock -// private CategoryCouponRepository categoryCouponRepository; -// -// @InjectMocks -// private CouponServiceImpl couponService; -// -// @BeforeEach -// void setUp() { -// MockitoAnnotations.openMocks(this); -// } -// -// @Test -// void testGetCategoryCoupons() { -// Long categoryId = 1L; -// PageRequest pageable = PageRequest.of(0, 10); -// Page categoryCoupons = new PageImpl<>(Collections.emptyList()); -// -// given(categoryCouponRepository.findByCategoryId(pageable, categoryId)).willReturn(categoryCoupons); -// -// Page result = categoryCouponRepository.findByCategoryId(pageable, categoryId); -// -// assertThat(result).isEqualTo(categoryCoupons); -// } -// -//// @Test -//// void testGetBookSpecificCoupon() { -//// Long couponId = 1L; -//// Coupon coupon = new Coupon(); -//// coupon.setId(couponId); -//// // Assume book coupon relationship setup -//// given(couponRepository.findById(couponId)).willReturn(Optional.of(coupon)); -//// -//// CouponInfoResponse response = couponService.getCouponDetails(couponId); -//// -//// assertThat(response).isNotNull(); -//// assertThat(response.couponId()).isEqualTo(couponId); -//// // Additional assertions for book-specific information -//// } -// -// @Test -// void testGetMemberCoupons() { -// Long memberId = 1L; -// PageRequest pageable = PageRequest.of(0, 10); -// Page memberCoupons = new PageImpl<>(Collections.emptyList()); -// -// given(memberCouponRepository.findByMemberId(pageable, memberId)).willReturn(memberCoupons); -// -// Page result = couponService.getUserCoupons(pageable, memberId); -// -// assertThat(result).isNotNull(); -// assertThat(result.getTotalElements()).isEqualTo(0); -// } -// -// @Test -// void testGetCouponHistory() { -// Long memberCouponId = 1L; -// PageRequest pageable = PageRequest.of(0, 10); -// Page history = new PageImpl<>(Collections.emptyList()); -// -// given(couponHistoryRepository.findByMemberCouponId(pageable, memberCouponId)).willReturn(history); -// -// Page result = couponService.getCouponHistory(pageable, memberCouponId); -// -// assertThat(result).isNotNull(); -// assertThat(result.getTotalElements()).isEqualTo(0); -// } -//}