From 621c9a297a0cf3e9a7f3d5e78b97c8408273fa5f Mon Sep 17 00:00:00 2001 From: Cho Sangwook <82208159+Sangwook02@users.noreply.github.com> Date: Thu, 30 Jan 2025 20:09:41 +0900 Subject: [PATCH] =?UTF-8?q?feat:=20=EC=88=98=EB=A3=8C=20=EC=B2=A0=ED=9A=8C?= =?UTF-8?q?=EC=8B=9C=20=EC=BF=A0=ED=8F=B0=20=ED=9A=8C=EC=88=98=20=EC=9D=B4?= =?UTF-8?q?=EB=B2=A4=ED=8A=B8=20=EA=B5=AC=ED=98=84=20(#857)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * feat: 수료 철회시 쿠폰 회수 이벤트 구현 * rename: 메서드 이름 변경 * refactor: where 조건 순서 변경 --- .../application/CouponEventHandler.java | 7 +++++++ .../coupon/application/CouponService.java | 19 ++++++++++++++++++- .../dao/IssuedCouponCustomRepository.java | 6 ++++++ .../dao/IssuedCouponCustomRepositoryImpl.java | 17 +++++++++++++++++ .../coupon/dao/IssuedCouponQueryMethod.java | 5 +++++ 5 files changed, 53 insertions(+), 1 deletion(-) diff --git a/src/main/java/com/gdschongik/gdsc/domain/coupon/application/CouponEventHandler.java b/src/main/java/com/gdschongik/gdsc/domain/coupon/application/CouponEventHandler.java index 1cb887b4b..1406e98a2 100644 --- a/src/main/java/com/gdschongik/gdsc/domain/coupon/application/CouponEventHandler.java +++ b/src/main/java/com/gdschongik/gdsc/domain/coupon/application/CouponEventHandler.java @@ -1,6 +1,7 @@ package com.gdschongik.gdsc.domain.coupon.application; import com.gdschongik.gdsc.domain.study.domain.StudyHistoriesCompletedEvent; +import com.gdschongik.gdsc.domain.study.domain.StudyHistoryCompletionWithdrawnEvent; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Component; @@ -20,4 +21,10 @@ public void handleStudyHistoryCompletedEvent(StudyHistoriesCompletedEvent event) log.info("[CouponEventHandler] 스터디 수료 이벤트 수신: studyHistoryIds={}", event.studyHistoryIds()); couponService.createAndIssueCouponByStudyHistories(event.studyHistoryIds()); } + + @TransactionalEventListener(phase = TransactionPhase.BEFORE_COMMIT) + public void handleStudyHistoryCompletionWithdrawnEvent(StudyHistoryCompletionWithdrawnEvent event) { + log.info("[CouponEventHandler] 스터디 수료 철회 이벤트 수신: studyHistoryId={}", event.studyHistoryId()); + couponService.revokeStudyCompletionCouponByStudyHistoryId(event.studyHistoryId()); + } } diff --git a/src/main/java/com/gdschongik/gdsc/domain/coupon/application/CouponService.java b/src/main/java/com/gdschongik/gdsc/domain/coupon/application/CouponService.java index 42e7eabc8..369eeed30 100644 --- a/src/main/java/com/gdschongik/gdsc/domain/coupon/application/CouponService.java +++ b/src/main/java/com/gdschongik/gdsc/domain/coupon/application/CouponService.java @@ -1,5 +1,6 @@ package com.gdschongik.gdsc.domain.coupon.application; +import static com.gdschongik.gdsc.domain.coupon.domain.CouponType.*; import static com.gdschongik.gdsc.global.exception.ErrorCode.*; import com.gdschongik.gdsc.domain.common.vo.Money; @@ -109,7 +110,7 @@ public void createAndIssueCouponByStudyHistories(List studyHistoryIds) { List students = memberRepository.findAllById(studentIds); Study study = studyHistories.get(0).getStudy(); - Coupon coupon = findOrCreate(CouponType.STUDY_COMPLETION, study); + Coupon coupon = findOrCreate(STUDY_COMPLETION, study); List issuedCoupons = students.stream() .map(student -> IssuedCoupon.create(coupon, student)) @@ -128,4 +129,20 @@ private Coupon findOrCreate(CouponType couponType, Study study) { return couponRepository.save(coupon); }); } + + @Transactional + public void revokeStudyCompletionCouponByStudyHistoryId(long studyHistoryId) { + StudyHistory studyHistory = studyHistoryRepository + .findById(studyHistoryId) + .orElseThrow(() -> new CustomException(STUDY_HISTORY_NOT_FOUND)); + + IssuedCoupon issuedCoupon = issuedCouponRepository + .findUnrevokedIssuedStudyCoupon(STUDY_COMPLETION, studyHistory.getStudent(), studyHistory.getStudy()) + .orElseThrow(() -> new CustomException(ISSUED_COUPON_NOT_FOUND)); + + issuedCoupon.revoke(); + issuedCouponRepository.save(issuedCoupon); + + log.info("[CouponService] 스터디 수료 쿠폰 회수: issuedCouponId={}", issuedCoupon.getId()); + } } diff --git a/src/main/java/com/gdschongik/gdsc/domain/coupon/dao/IssuedCouponCustomRepository.java b/src/main/java/com/gdschongik/gdsc/domain/coupon/dao/IssuedCouponCustomRepository.java index 32df4a7bf..d16e58502 100644 --- a/src/main/java/com/gdschongik/gdsc/domain/coupon/dao/IssuedCouponCustomRepository.java +++ b/src/main/java/com/gdschongik/gdsc/domain/coupon/dao/IssuedCouponCustomRepository.java @@ -1,11 +1,17 @@ package com.gdschongik.gdsc.domain.coupon.dao; +import com.gdschongik.gdsc.domain.coupon.domain.CouponType; import com.gdschongik.gdsc.domain.coupon.domain.IssuedCoupon; import com.gdschongik.gdsc.domain.coupon.dto.request.IssuedCouponQueryOption; +import com.gdschongik.gdsc.domain.member.domain.Member; +import com.gdschongik.gdsc.domain.study.domain.Study; +import java.util.Optional; import org.springframework.data.domain.Page; import org.springframework.data.domain.Pageable; public interface IssuedCouponCustomRepository { Page findAllIssuedCoupons(IssuedCouponQueryOption queryOption, Pageable pageable); + + Optional findUnrevokedIssuedStudyCoupon(CouponType couponType, Member member, Study study); } diff --git a/src/main/java/com/gdschongik/gdsc/domain/coupon/dao/IssuedCouponCustomRepositoryImpl.java b/src/main/java/com/gdschongik/gdsc/domain/coupon/dao/IssuedCouponCustomRepositoryImpl.java index 5b4777fba..08123cd75 100644 --- a/src/main/java/com/gdschongik/gdsc/domain/coupon/dao/IssuedCouponCustomRepositoryImpl.java +++ b/src/main/java/com/gdschongik/gdsc/domain/coupon/dao/IssuedCouponCustomRepositoryImpl.java @@ -1,12 +1,17 @@ package com.gdschongik.gdsc.domain.coupon.dao; +import static com.gdschongik.gdsc.domain.coupon.domain.QCoupon.*; import static com.gdschongik.gdsc.domain.coupon.domain.QIssuedCoupon.issuedCoupon; +import com.gdschongik.gdsc.domain.coupon.domain.CouponType; import com.gdschongik.gdsc.domain.coupon.domain.IssuedCoupon; import com.gdschongik.gdsc.domain.coupon.dto.request.IssuedCouponQueryOption; +import com.gdschongik.gdsc.domain.member.domain.Member; +import com.gdschongik.gdsc.domain.study.domain.Study; import com.querydsl.jpa.impl.JPAQuery; import com.querydsl.jpa.impl.JPAQueryFactory; import java.util.List; +import java.util.Optional; import lombok.RequiredArgsConstructor; import org.springframework.data.domain.Page; import org.springframework.data.domain.Pageable; @@ -32,4 +37,16 @@ public Page findAllIssuedCoupons(IssuedCouponQueryOption queryOpti return PageableExecutionUtils.getPage(fetch, pageable, countQuery::fetchOne); } + + @Override + public Optional findUnrevokedIssuedStudyCoupon(CouponType couponType, Member member, Study study) { + return Optional.ofNullable(queryFactory + .selectFrom(issuedCoupon) + .leftJoin(issuedCoupon.coupon, coupon) + .where(eqMember(member) + .and(coupon.study.eq(study)) + .and(hasRevoked(false)) + .and(coupon.couponType.eq(couponType))) + .fetchFirst()); + } } diff --git a/src/main/java/com/gdschongik/gdsc/domain/coupon/dao/IssuedCouponQueryMethod.java b/src/main/java/com/gdschongik/gdsc/domain/coupon/dao/IssuedCouponQueryMethod.java index 8ced8d892..c88baae9f 100644 --- a/src/main/java/com/gdschongik/gdsc/domain/coupon/dao/IssuedCouponQueryMethod.java +++ b/src/main/java/com/gdschongik/gdsc/domain/coupon/dao/IssuedCouponQueryMethod.java @@ -3,6 +3,7 @@ import static com.gdschongik.gdsc.domain.coupon.domain.QIssuedCoupon.issuedCoupon; import com.gdschongik.gdsc.domain.coupon.dto.request.IssuedCouponQueryOption; +import com.gdschongik.gdsc.domain.member.domain.Member; import com.querydsl.core.BooleanBuilder; import com.querydsl.core.types.dsl.BooleanExpression; @@ -16,6 +17,10 @@ default BooleanExpression eqMemberName(String memberName) { return memberName != null ? issuedCoupon.coupon.name.containsIgnoreCase(memberName) : null; } + default BooleanExpression eqMember(Member member) { + return member != null ? issuedCoupon.member.eq(member) : null; + } + default BooleanExpression eqPhone(String phone) { return phone != null ? issuedCoupon.member.phone.contains(phone.replaceAll("-", "")) : null; }