From b8e1adabdc61c861bd0ea1c16b5d5c989e50e966 Mon Sep 17 00:00:00 2001 From: HyuckJuneHong Date: Wed, 29 Nov 2023 17:58:04 +0900 Subject: [PATCH 1/5] =?UTF-8?q?refactor:=20coupon=20=EB=B0=9C=ED=96=89=20?= =?UTF-8?q?=EB=B0=8F=20=EC=82=AD=EC=A0=9C=20=EC=8A=A4=ED=83=80=EC=9D=BC=20?= =?UTF-8?q?=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../api/application/coupon/CouponService.java | 31 ++++++----- .../api/presentation/CouponController.java | 8 +-- .../application/coupon/CouponServiceTest.java | 51 ++++++------------- 3 files changed, 36 insertions(+), 54 deletions(-) diff --git a/src/main/java/com/moabam/api/application/coupon/CouponService.java b/src/main/java/com/moabam/api/application/coupon/CouponService.java index a25af06e..660cc9df 100644 --- a/src/main/java/com/moabam/api/application/coupon/CouponService.java +++ b/src/main/java/com/moabam/api/application/coupon/CouponService.java @@ -42,16 +42,28 @@ public class CouponService { private final CouponWalletSearchRepository couponWalletSearchRepository; @Transactional - public void create(AuthMember admin, CreateCouponRequest request) { - validateAdminRole(admin); + public void create(CreateCouponRequest request, Long adminId, Role role) { + validateAdminRole(role); validateConflictName(request.name()); validateConflictStartAt(request.startAt()); validatePeriod(request.startAt(), request.openAt()); - Coupon coupon = CouponMapper.toEntity(admin.id(), request); + Coupon coupon = CouponMapper.toEntity(adminId, request); + couponRepository.save(coupon); } + @Transactional + public void delete(Long couponId, Role role) { + validateAdminRole(role); + + Coupon coupon = couponRepository.findById(couponId) + .orElseThrow(() -> new NotFoundException(ErrorMessage.NOT_FOUND_COUPON)); + + couponRepository.delete(coupon); + couponManageService.deleteQueue(coupon.getName()); + } + @Transactional public void use(Long memberId, Long couponWalletId) { CouponWallet couponWallet = getWalletByIdAndMemberId(couponWalletId, memberId); @@ -74,15 +86,6 @@ public void discount(Long memberId, Long couponWalletId) { couponWalletRepository.delete(couponWallet); } - @Transactional - public void delete(AuthMember admin, Long couponId) { - validateAdminRole(admin); - Coupon coupon = couponRepository.findById(couponId) - .orElseThrow(() -> new NotFoundException(ErrorMessage.NOT_FOUND_COUPON)); - couponRepository.delete(coupon); - couponManageService.deleteQueue(coupon.getName()); - } - public CouponResponse getById(Long couponId) { Coupon coupon = couponRepository.findById(couponId) .orElseThrow(() -> new NotFoundException(ErrorMessage.NOT_FOUND_COUPON)); @@ -121,8 +124,8 @@ private void validatePeriod(LocalDate startAt, LocalDate openAt) { } } - private void validateAdminRole(AuthMember admin) { - if (!admin.role().equals(Role.ADMIN)) { + private void validateAdminRole(Role role) { + if (!role.equals(Role.ADMIN)) { throw new NotFoundException(ErrorMessage.MEMBER_NOT_FOUND); } } diff --git a/src/main/java/com/moabam/api/presentation/CouponController.java b/src/main/java/com/moabam/api/presentation/CouponController.java index 4668e431..bafe0cf1 100644 --- a/src/main/java/com/moabam/api/presentation/CouponController.java +++ b/src/main/java/com/moabam/api/presentation/CouponController.java @@ -33,14 +33,14 @@ public class CouponController { @PostMapping("/admins/coupons") @ResponseStatus(HttpStatus.CREATED) - public void create(@Auth AuthMember admin, @Valid @RequestBody CreateCouponRequest request) { - couponService.create(admin, request); + public void create(@Valid @RequestBody CreateCouponRequest request, @Auth AuthMember admin) { + couponService.create(request, admin.id(), admin.role()); } @DeleteMapping("/admins/coupons/{couponId}") @ResponseStatus(HttpStatus.OK) - public void delete(@Auth AuthMember admin, @PathVariable("couponId") Long couponId) { - couponService.delete(admin, couponId); + public void delete(@PathVariable("couponId") Long couponId, @Auth AuthMember admin) { + couponService.delete(couponId, admin.role()); } @GetMapping("/coupons/{couponId}") diff --git a/src/test/java/com/moabam/api/application/coupon/CouponServiceTest.java b/src/test/java/com/moabam/api/application/coupon/CouponServiceTest.java index 6e1efbe9..213df536 100644 --- a/src/test/java/com/moabam/api/application/coupon/CouponServiceTest.java +++ b/src/test/java/com/moabam/api/application/coupon/CouponServiceTest.java @@ -18,6 +18,7 @@ import org.mockito.junit.jupiter.MockitoExtension; import com.moabam.api.application.bug.BugService; +import com.moabam.api.domain.bug.BugType; import com.moabam.api.domain.coupon.Coupon; import com.moabam.api.domain.coupon.CouponType; import com.moabam.api.domain.coupon.CouponWallet; @@ -48,10 +49,10 @@ class CouponServiceTest { CouponService couponService; @Mock - CouponManageService couponManageService; + BugService bugService; @Mock - BugService bugService; + CouponManageService couponManageService; @Mock CouponRepository couponRepository; @@ -68,94 +69,82 @@ class CouponServiceTest { @Mock ClockHolder clockHolder; - @WithMember(role = Role.ADMIN) - @DisplayName("쿠폰을 성공적으로 발행한다. - Void") + @DisplayName("관리자가 쿠폰을 성공적으로 발행한다. - Void") @Test void create_success() { // Given - AuthMember admin = AuthorizationThreadLocal.getAuthMember(); CreateCouponRequest request = CouponFixture.createCouponRequest(); given(couponRepository.existsByName(any(String.class))).willReturn(false); given(clockHolder.date()).willReturn(LocalDate.of(2022, 1, 1)); // When - couponService.create(admin, request); + couponService.create(request, 1L, Role.ADMIN); // Then verify(couponRepository).save(any(Coupon.class)); } - @WithMember(role = Role.USER) @DisplayName("권한 없는 사용자가 쿠폰을 발행한다. - NotFoundException") @Test void create_Admin_NotFoundException() { // Given - AuthMember admin = AuthorizationThreadLocal.getAuthMember(); CreateCouponRequest request = CouponFixture.createCouponRequest(); // When & Then - assertThatThrownBy(() -> couponService.create(admin, request)) + assertThatThrownBy(() -> couponService.create(request, 1L, Role.USER)) .isInstanceOf(NotFoundException.class) .hasMessage(ErrorMessage.MEMBER_NOT_FOUND.getMessage()); } - @WithMember(role = Role.ADMIN) @DisplayName("존재하지 않는 쿠폰 종류를 발행한다. - NotFoundException") @Test void create_Type_NotFoundException() { // Given - AuthMember admin = AuthorizationThreadLocal.getAuthMember(); CreateCouponRequest request = CouponFixture.createCouponRequest("UNKNOWN", 2, 1); given(couponRepository.existsByName(any(String.class))).willReturn(false); given(clockHolder.date()).willReturn(LocalDate.of(2022, 1, 1)); // When & Then - assertThatThrownBy(() -> couponService.create(admin, request)) + assertThatThrownBy(() -> couponService.create(request, 1L, Role.ADMIN)) .isInstanceOf(NotFoundException.class) .hasMessage(ErrorMessage.NOT_FOUND_COUPON_TYPE.getMessage()); } - @WithMember(role = Role.ADMIN) @DisplayName("중복된 쿠폰명을 발행한다. - ConflictException") @Test void create_Name_ConflictException() { // Given - AuthMember admin = AuthorizationThreadLocal.getAuthMember(); CreateCouponRequest request = CouponFixture.createCouponRequest(); given(couponRepository.existsByName(any(String.class))).willReturn(true); // When & Then - assertThatThrownBy(() -> couponService.create(admin, request)) + assertThatThrownBy(() -> couponService.create(request, 1L, Role.ADMIN)) .isInstanceOf(ConflictException.class) .hasMessage(ErrorMessage.CONFLICT_COUPON_NAME.getMessage()); } - @WithMember(role = Role.ADMIN) @DisplayName("중복된 쿠폰 발행 가능 날짜를 발행한다. - ConflictException") @Test void create_StartAt_ConflictException() { // Given - AuthMember admin = AuthorizationThreadLocal.getAuthMember(); CreateCouponRequest request = CouponFixture.createCouponRequest(); given(couponRepository.existsByName(any(String.class))).willReturn(false); given(couponRepository.existsByStartAt(any(LocalDate.class))).willReturn(true); // When & Then - assertThatThrownBy(() -> couponService.create(admin, request)) + assertThatThrownBy(() -> couponService.create(request, 1L, Role.ADMIN)) .isInstanceOf(ConflictException.class) .hasMessage(ErrorMessage.CONFLICT_COUPON_START_AT.getMessage()); } - @WithMember(role = Role.ADMIN) @DisplayName("현재 날짜가 쿠폰 발급 가능 날짜와 같거나 이후이다. - BadRequestException") @Test void create_StartAt_BadRequestException() { // Given - AuthMember admin = AuthorizationThreadLocal.getAuthMember(); CreateCouponRequest request = CouponFixture.createCouponRequest(); given(clockHolder.date()).willReturn(LocalDate.of(2025, 1, 1)); @@ -163,17 +152,15 @@ void create_StartAt_BadRequestException() { given(couponRepository.existsByStartAt(any(LocalDate.class))).willReturn(false); // When & Then - assertThatThrownBy(() -> couponService.create(admin, request)) + assertThatThrownBy(() -> couponService.create(request, 1L, Role.ADMIN)) .isInstanceOf(BadRequestException.class) .hasMessage(ErrorMessage.INVALID_COUPON_START_AT_PERIOD.getMessage()); } - @WithMember(role = Role.ADMIN) @DisplayName("쿠폰 정보 오픈 날짜가 쿠폰 발급 시작 날짜와 같거나 이후인 쿠폰을 발행한다. - BadRequestException") @Test void create_OpenAt_BadRequestException() { // Given - AuthMember admin = AuthorizationThreadLocal.getAuthMember(); String couponType = CouponType.GOLDEN.getName(); CreateCouponRequest request = CouponFixture.createCouponRequest(couponType, 1, 1); @@ -182,53 +169,44 @@ void create_OpenAt_BadRequestException() { given(clockHolder.date()).willReturn(LocalDate.of(2022, 1, 1)); // When & Then - assertThatThrownBy(() -> couponService.create(admin, request)) + assertThatThrownBy(() -> couponService.create(request, 1L, Role.ADMIN)) .isInstanceOf(BadRequestException.class) .hasMessage(ErrorMessage.INVALID_COUPON_OPEN_AT_PERIOD.getMessage()); } - @WithMember(role = Role.ADMIN) @DisplayName("쿠폰 아이디와 일치하는 쿠폰을 성공적으로 삭제한다. - Void") @Test void delete_success() { // Given - AuthMember admin = AuthorizationThreadLocal.getAuthMember(); Coupon coupon = CouponFixture.coupon(10, 100); given(couponRepository.findById(any(Long.class))).willReturn(Optional.of(coupon)); // When - couponService.delete(admin, 1L); + couponService.delete(1L, Role.ADMIN); // Then verify(couponRepository).delete(coupon); verify(couponManageService).deleteQueue(any(String.class)); } - @WithMember(role = Role.USER) @DisplayName("권한 없는 사용자가 쿠폰을 삭제한다. - NotFoundException") @Test void delete_Admin_NotFoundException() { - // Given - AuthMember admin = AuthorizationThreadLocal.getAuthMember(); - // When & Then - assertThatThrownBy(() -> couponService.delete(admin, 1L)) + assertThatThrownBy(() -> couponService.delete(1L, Role.USER)) .isInstanceOf(NotFoundException.class) .hasMessage(ErrorMessage.MEMBER_NOT_FOUND.getMessage()); } - @WithMember(role = Role.ADMIN) @DisplayName("존재하지 않는 쿠폰 아이디를 삭제하려고 시도한다. - NotFoundException") @Test void delete_NotFoundException() { // Given - AuthMember admin = AuthorizationThreadLocal.getAuthMember(); - given(couponRepository.findById(any(Long.class))).willReturn(Optional.empty()); // When & Then - assertThatThrownBy(() -> couponService.delete(admin, 1L)) + assertThatThrownBy(() -> couponService.delete(1L, Role.ADMIN)) .isInstanceOf(NotFoundException.class) .hasMessage(ErrorMessage.NOT_FOUND_COUPON.getMessage()); } @@ -329,6 +307,7 @@ void use_success() { // Then verify(couponWalletRepository).delete(any(CouponWallet.class)); + verify(bugService).applyCoupon(any(Long.class), any(BugType.class), any(int.class)); } @DisplayName("특정 회원이 쿠폰 지갑에 가지고 있는 할인 쿠폰을 사용한다. - BadRequestException") From e4ef97d4da554c04875b0871e1dcc59544f3ff65 Mon Sep 17 00:00:00 2001 From: HyuckJuneHong Date: Wed, 29 Nov 2023 18:35:02 +0900 Subject: [PATCH 2/5] =?UTF-8?q?refactor:=20My=20Coupon=20=EC=A1=B0?= =?UTF-8?q?=ED=9A=8C=20=EC=BD=94=EB=93=9C=20=EA=B0=9C=EC=84=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../api/application/coupon/CouponMapper.java | 1 + .../api/application/coupon/CouponService.java | 16 +- .../CouponWalletSearchRepository.java | 8 +- .../api/dto/coupon/MyCouponResponse.java | 1 + .../api/presentation/CouponController.java | 22 +- .../application/coupon/CouponServiceTest.java | 31 ++- .../CouponWalletSearchRepositoryTest.java | 19 +- .../presentation/CouponControllerTest.java | 188 +++++++++--------- .../support/snippet/CouponWalletSnippet.java | 1 + 9 files changed, 141 insertions(+), 146 deletions(-) diff --git a/src/main/java/com/moabam/api/application/coupon/CouponMapper.java b/src/main/java/com/moabam/api/application/coupon/CouponMapper.java index ef8eb15a..d8799ab1 100644 --- a/src/main/java/com/moabam/api/application/coupon/CouponMapper.java +++ b/src/main/java/com/moabam/api/application/coupon/CouponMapper.java @@ -52,6 +52,7 @@ public static MyCouponResponse toMyResponse(CouponWallet couponWallet) { Coupon coupon = couponWallet.getCoupon(); return MyCouponResponse.builder() + .walletId(couponWallet.getId()) .id(coupon.getId()) .name(coupon.getName()) .description(coupon.getDescription()) diff --git a/src/main/java/com/moabam/api/application/coupon/CouponService.java b/src/main/java/com/moabam/api/application/coupon/CouponService.java index 660cc9df..17ccb5a3 100644 --- a/src/main/java/com/moabam/api/application/coupon/CouponService.java +++ b/src/main/java/com/moabam/api/application/coupon/CouponService.java @@ -19,7 +19,6 @@ import com.moabam.api.dto.coupon.CouponStatusRequest; import com.moabam.api.dto.coupon.CreateCouponRequest; import com.moabam.api.dto.coupon.MyCouponResponse; -import com.moabam.global.auth.model.AuthMember; import com.moabam.global.common.util.ClockHolder; import com.moabam.global.error.exception.BadRequestException; import com.moabam.global.error.exception.ConflictException; @@ -66,7 +65,8 @@ public void delete(Long couponId, Role role) { @Transactional public void use(Long memberId, Long couponWalletId) { - CouponWallet couponWallet = getWalletByIdAndMemberId(couponWalletId, memberId); + CouponWallet couponWallet = couponWalletSearchRepository.findByIdAndMemberId(couponWalletId, memberId) + .orElseThrow(() -> new NotFoundException(ErrorMessage.NOT_FOUND_COUPON_WALLET)); Coupon coupon = couponWallet.getCoupon(); BugType bugType = coupon.getType().getBugType(); @@ -76,7 +76,8 @@ public void use(Long memberId, Long couponWalletId) { @Transactional public void discount(Long memberId, Long couponWalletId) { - CouponWallet couponWallet = getWalletByIdAndMemberId(couponWalletId, memberId); + CouponWallet couponWallet = couponWalletSearchRepository.findByIdAndMemberId(couponWalletId, memberId) + .orElseThrow(() -> new NotFoundException(ErrorMessage.NOT_FOUND_COUPON_WALLET)); Coupon coupon = couponWallet.getCoupon(); if (!coupon.getType().isDiscount()) { @@ -100,18 +101,13 @@ public List getAllByStatus(CouponStatusRequest request) { return CouponMapper.toResponses(coupons); } - public List getWallet(Long couponId, AuthMember authMember) { + public List getAllByWalletIdAndMemberId(Long couponWalletId, Long memberId) { List couponWallets = - couponWalletSearchRepository.findAllByCouponIdAndMemberId(couponId, authMember.id()); + couponWalletSearchRepository.findAllByIdAndMemberId(couponWalletId, memberId); return CouponMapper.toMyResponses(couponWallets); } - public CouponWallet getWalletByIdAndMemberId(Long couponWalletId, Long memberId) { - return couponWalletSearchRepository.findByIdAndMemberId(couponWalletId, memberId) - .orElseThrow(() -> new NotFoundException(ErrorMessage.NOT_FOUND_COUPON_WALLET)); - } - private void validatePeriod(LocalDate startAt, LocalDate openAt) { LocalDate now = clockHolder.date(); diff --git a/src/main/java/com/moabam/api/domain/coupon/repository/CouponWalletSearchRepository.java b/src/main/java/com/moabam/api/domain/coupon/repository/CouponWalletSearchRepository.java index afcdbf16..9a931864 100644 --- a/src/main/java/com/moabam/api/domain/coupon/repository/CouponWalletSearchRepository.java +++ b/src/main/java/com/moabam/api/domain/coupon/repository/CouponWalletSearchRepository.java @@ -20,23 +20,23 @@ public class CouponWalletSearchRepository { private final JPAQueryFactory jpaQueryFactory; - public List findAllByCouponIdAndMemberId(Long couponId, Long memberId) { + public List findAllByIdAndMemberId(Long couponWalletId, Long memberId) { return jpaQueryFactory .selectFrom(couponWallet) .join(couponWallet.coupon, coupon).fetchJoin() .where( - DynamicQuery.generateEq(couponId, couponWallet.coupon.id::eq), + DynamicQuery.generateEq(couponWalletId, couponWallet.id::eq), DynamicQuery.generateEq(memberId, couponWallet.memberId::eq) ) .fetch(); } - public Optional findByIdAndMemberId(Long id, Long memberId) { + public Optional findByIdAndMemberId(Long couponWalletId, Long memberId) { return Optional.ofNullable(jpaQueryFactory .selectFrom(couponWallet) .join(couponWallet.coupon, coupon).fetchJoin() .where( - couponWallet.id.eq(id), + couponWallet.id.eq(couponWalletId), couponWallet.memberId.eq(memberId)) .fetchOne() ); diff --git a/src/main/java/com/moabam/api/dto/coupon/MyCouponResponse.java b/src/main/java/com/moabam/api/dto/coupon/MyCouponResponse.java index a0201ebb..60859b7a 100644 --- a/src/main/java/com/moabam/api/dto/coupon/MyCouponResponse.java +++ b/src/main/java/com/moabam/api/dto/coupon/MyCouponResponse.java @@ -6,6 +6,7 @@ @Builder public record MyCouponResponse( + Long walletId, Long id, String name, String description, diff --git a/src/main/java/com/moabam/api/presentation/CouponController.java b/src/main/java/com/moabam/api/presentation/CouponController.java index bafe0cf1..d3909d65 100644 --- a/src/main/java/com/moabam/api/presentation/CouponController.java +++ b/src/main/java/com/moabam/api/presentation/CouponController.java @@ -55,17 +55,13 @@ public List getAllByStatus(@Valid @RequestBody CouponStatusReque return couponService.getAllByStatus(request); } - @PostMapping("/coupons") - @ResponseStatus(HttpStatus.OK) - public void registerQueue(@Auth AuthMember authMember, @RequestParam("couponName") String couponName) { - couponManageService.registerQueue(authMember.id(), couponName); - } - - @GetMapping({"/my-coupons", "/my-coupons/{couponId}"}) + @GetMapping({"/my-coupons", "/my-coupons/{couponWalletId}"}) @ResponseStatus(HttpStatus.OK) - public List getWallet(@Auth AuthMember authMember, - @PathVariable(value = "couponId", required = false) Long couponId) { - return couponService.getWallet(couponId, authMember); + public List getAllByWalletIdAndMemberId( + @PathVariable(value = "couponWalletId", required = false) Long couponWalletId, + @Auth AuthMember authMember + ) { + return couponService.getAllByWalletIdAndMemberId(couponWalletId, authMember.id()); } @PostMapping("/my-coupons/{couponWalletId}") @@ -73,4 +69,10 @@ public List getWallet(@Auth AuthMember authMember, public void use(@Auth AuthMember authMember, @PathVariable("couponWalletId") Long couponWalletId) { couponService.use(authMember.id(), couponWalletId); } + + @PostMapping("/coupons") + @ResponseStatus(HttpStatus.OK) + public void registerQueue(@Auth AuthMember authMember, @RequestParam("couponName") String couponName) { + couponManageService.registerQueue(authMember.id(), couponName); + } } diff --git a/src/test/java/com/moabam/api/application/coupon/CouponServiceTest.java b/src/test/java/com/moabam/api/application/coupon/CouponServiceTest.java index 213df536..449fb849 100644 --- a/src/test/java/com/moabam/api/application/coupon/CouponServiceTest.java +++ b/src/test/java/com/moabam/api/application/coupon/CouponServiceTest.java @@ -1,6 +1,5 @@ package com.moabam.api.application.coupon; -import static com.moabam.support.fixture.CouponFixture.*; import static org.assertj.core.api.Assertions.*; import static org.mockito.BDDMockito.*; @@ -31,14 +30,11 @@ import com.moabam.api.dto.coupon.CouponStatusRequest; import com.moabam.api.dto.coupon.CreateCouponRequest; import com.moabam.api.dto.coupon.MyCouponResponse; -import com.moabam.global.auth.model.AuthMember; -import com.moabam.global.auth.model.AuthorizationThreadLocal; import com.moabam.global.common.util.ClockHolder; import com.moabam.global.error.exception.BadRequestException; import com.moabam.global.error.exception.ConflictException; import com.moabam.global.error.exception.NotFoundException; import com.moabam.global.error.model.ErrorMessage; -import com.moabam.support.annotation.WithMember; import com.moabam.support.common.FilterProcessExtension; import com.moabam.support.fixture.CouponFixture; @@ -257,39 +253,36 @@ void getAllByStatus_success(List coupons) { assertThat(actual).hasSize(coupons.size()); } - @WithMember - @DisplayName("나의 쿠폰함을 성공적으로 조회한다.") + @DisplayName("나의 모든 쿠폰을 성공적으로 조회한다.") @MethodSource("com.moabam.support.fixture.CouponWalletFixture#provideCouponWalletByCouponId1_total5") @ParameterizedTest - void getWallet_success(List couponWallets) { + void getAllByWalletIdAndMemberId_all_success(List couponWallets) { // Given - AuthMember authMember = AuthorizationThreadLocal.getAuthMember(); - - given(couponWalletSearchRepository.findAllByCouponIdAndMemberId(isNull(), any(Long.class))) + given(couponWalletSearchRepository.findAllByIdAndMemberId(isNull(), any(Long.class))) .willReturn(couponWallets); // When - List actual = couponService.getWallet(null, authMember); + List actual = couponService.getAllByWalletIdAndMemberId(null, 1L); // Then assertThat(actual).hasSize(couponWallets.size()); } - @WithMember - @DisplayName("지갑에서 특정 쿠폰을 성공적으로 조회한다.") + @DisplayName("나의 특정 쿠폰을 성공적으로 조회한다.") @Test - void getByWalletIdAndMemberId_success() { + void getAllByWalletIdAndMemberId_success() { // Given - CouponWallet couponWallet = CouponWallet.create(1L, coupon()); + Coupon coupon = CouponFixture.coupon(); + List couponWallets = List.of(CouponWallet.create(1L, coupon)); - given(couponWalletSearchRepository.findByIdAndMemberId(any(Long.class), any(Long.class))) - .willReturn(Optional.of(couponWallet)); + given(couponWalletSearchRepository.findAllByIdAndMemberId(any(Long.class), any(Long.class))) + .willReturn(couponWallets); // When - CouponWallet actual = couponService.getWalletByIdAndMemberId(1L, 1L); + List actual = couponService.getAllByWalletIdAndMemberId(1L, 1L); // Then - assertThat(actual.getCoupon().getName()).isEqualTo(couponWallet.getCoupon().getName()); + assertThat(actual).hasSize(1); } @DisplayName("특정 회원이 쿠폰 지갑에 가지고 있는 특정 쿠폰을 성공적으로 사용한다. - Void") diff --git a/src/test/java/com/moabam/api/domain/coupon/repository/CouponWalletSearchRepositoryTest.java b/src/test/java/com/moabam/api/domain/coupon/repository/CouponWalletSearchRepositoryTest.java index 82d1fd5c..2770be82 100644 --- a/src/test/java/com/moabam/api/domain/coupon/repository/CouponWalletSearchRepositoryTest.java +++ b/src/test/java/com/moabam/api/domain/coupon/repository/CouponWalletSearchRepositoryTest.java @@ -31,15 +31,16 @@ class CouponWalletSearchRepositoryTest { @Autowired private CouponWalletSearchRepository couponWalletSearchRepository; - @DisplayName("나의 쿠폰함의 특정 쿠폰을 조회한다.. - List") + @DisplayName("나의 쿠폰함의 특정 쿠폰을 조회한다. - List") @Test - void findAllByCouponIdAndMemberId_success() { + void findAllByIdAndMemberId_success() { // Given Coupon coupon = couponRepository.save(CouponFixture.coupon()); CouponWallet couponWallet = couponWalletRepository.save(CouponWallet.create(1L, coupon)); // When - List actual = couponWalletSearchRepository.findAllByCouponIdAndMemberId(coupon.getId(), 1L); + List actual = couponWalletSearchRepository + .findAllByIdAndMemberId(couponWallet.getId(), 1L); // Then assertThat(actual).hasSize(1); @@ -50,7 +51,7 @@ void findAllByCouponIdAndMemberId_success() { @DisplayName("ID가 1인 회원은 쿠폰 1개를 가지고 있다. - List") @MethodSource("com.moabam.support.fixture.CouponWalletFixture#provideCouponWalletAll") @ParameterizedTest - void findAllByCouponIdAndMemberId_Id1_success(List couponWallets) { + void findAllByIdAndMemberId_Id1_success(List couponWallets) { // Given couponWallets.forEach(couponWallet -> { Coupon coupon = couponRepository.save(couponWallet.getCoupon()); @@ -58,7 +59,7 @@ void findAllByCouponIdAndMemberId_Id1_success(List couponWallets) }); // When - List actual = couponWalletSearchRepository.findAllByCouponIdAndMemberId(null, 1L); + List actual = couponWalletSearchRepository.findAllByIdAndMemberId(null, 1L); // Then assertThat(actual).hasSize(1); @@ -67,7 +68,7 @@ void findAllByCouponIdAndMemberId_Id1_success(List couponWallets) @DisplayName("ID가 2인 회원은 쿠폰 ID가 777인 쿠폰을 가지고 있지 않다. - List") @MethodSource("com.moabam.support.fixture.CouponWalletFixture#provideCouponWalletAll") @ParameterizedTest - void findAllByCouponIdAndMemberId_Id2_notCouponId777(List couponWallets) { + void findAllByIdAndMemberId_Id2_notCouponId777(List couponWallets) { // Given couponWallets.forEach(couponWallet -> { Coupon coupon = couponRepository.save(couponWallet.getCoupon()); @@ -75,7 +76,7 @@ void findAllByCouponIdAndMemberId_Id2_notCouponId777(List couponWa }); // When - List actual = couponWalletSearchRepository.findAllByCouponIdAndMemberId(777L, 2L); + List actual = couponWalletSearchRepository.findAllByIdAndMemberId(777L, 2L); // Then assertThat(actual).isEmpty(); @@ -84,7 +85,7 @@ void findAllByCouponIdAndMemberId_Id2_notCouponId777(List couponWa @DisplayName("ID가 3인 회원은 쿠폰 3개를 가지고 있다. - List") @MethodSource("com.moabam.support.fixture.CouponWalletFixture#provideCouponWalletAll") @ParameterizedTest - void findAllByCouponIdAndMemberId_Id3_success(List couponWallets) { + void findAllByIdAndMemberId_Id3_success(List couponWallets) { // Given couponWallets.forEach(couponWallet -> { Coupon coupon = couponRepository.save(couponWallet.getCoupon()); @@ -92,7 +93,7 @@ void findAllByCouponIdAndMemberId_Id3_success(List couponWallets) }); // When - List actual = couponWalletSearchRepository.findAllByCouponIdAndMemberId(null, 3L); + List actual = couponWalletSearchRepository.findAllByIdAndMemberId(null, 3L); // Then assertThat(actual).hasSize(3); diff --git a/src/test/java/com/moabam/api/presentation/CouponControllerTest.java b/src/test/java/com/moabam/api/presentation/CouponControllerTest.java index 2928f6e7..12d167d9 100644 --- a/src/test/java/com/moabam/api/presentation/CouponControllerTest.java +++ b/src/test/java/com/moabam/api/presentation/CouponControllerTest.java @@ -301,105 +301,16 @@ void getAllByStatus_Coupon_success(List coupons) throws Exception { .andExpect(jsonPath("$", hasSize(1))); } - @Disabled - @WithMember - @DisplayName("POST - 쿠폰 발급 요청을 성공적으로 한다. - Void") - @Test - void registerQueue_success() throws Exception { - // Given - Coupon couponFixture = CouponFixture.coupon(); - Coupon coupon = couponRepository.save(couponFixture); - - given(clockHolder.date()).willReturn(LocalDate.of(2023, 2, 1)); - - // When & Then - mockMvc.perform(post("/coupons") - .param("couponName", coupon.getName())) - .andDo(print()) - .andDo(document("coupons", - preprocessRequest(prettyPrint()), - preprocessResponse(prettyPrint()))) - .andExpect(status().isOk()); - } - - @WithMember - @DisplayName("POST - 발급 가능 날짜가 아닌 쿠폰에 발급 요청을 한다. (Not found) - BadRequestException") - @Test - void registerQueue_Zero_StartAt_BadRequestException() throws Exception { - // Given - Coupon couponFixture = CouponFixture.coupon(); - Coupon coupon = couponRepository.save(couponFixture); - - given(clockHolder.date()).willReturn(LocalDate.of(2022, 1, 1)); - - // When & Then - mockMvc.perform(post("/coupons") - .param("couponName", coupon.getName())) - .andDo(print()) - .andDo(document("coupons", - preprocessRequest(prettyPrint()), - preprocessResponse(prettyPrint()), - ErrorSnippet.ERROR_MESSAGE_RESPONSE)) - .andExpect(status().isBadRequest()) - .andExpect(content().contentType(MediaType.APPLICATION_JSON)) - .andExpect(jsonPath("$.message").value(ErrorMessage.INVALID_COUPON_PERIOD.getMessage())); - } - - @WithMember - @DisplayName("POST - 발급 가능 날짜가 아닌 쿠폰에 발급 요청을 한다. (Not equals) - BadRequestException") - @Test - void registerQueue_Not_StartAt_BadRequestException() throws Exception { - // Given - Coupon couponFixture = CouponFixture.coupon(); - couponRepository.save(couponFixture); - - given(clockHolder.date()).willReturn(LocalDate.of(2022, 2, 1)); - - // When & Then - mockMvc.perform(post("/coupons") - .param("couponName", "not start couponName")) - .andDo(print()) - .andDo(document("coupons", - preprocessRequest(prettyPrint()), - preprocessResponse(prettyPrint()), - ErrorSnippet.ERROR_MESSAGE_RESPONSE)) - .andExpect(status().isBadRequest()) - .andExpect(content().contentType(MediaType.APPLICATION_JSON)) - .andExpect(jsonPath("$.message").value(ErrorMessage.INVALID_COUPON_PERIOD.getMessage())); - } - - @WithMember - @DisplayName("POST - 존재하지 않는 쿠폰에 발급 요청을 한다. - NotFoundException") - @Test - void registerQueue_NotFoundException() throws Exception { - // Given - Coupon coupon = CouponFixture.coupon("Not found couponName", 2, 1); - - given(clockHolder.date()).willReturn(LocalDate.of(2023, 2, 1)); - - // When & Then - mockMvc.perform(post("/coupons") - .param("couponName", coupon.getName())) - .andDo(print()) - .andDo(document("coupons", - preprocessRequest(prettyPrint()), - preprocessResponse(prettyPrint()), - ErrorSnippet.ERROR_MESSAGE_RESPONSE)) - .andExpect(status().isBadRequest()) - .andExpect(content().contentType(MediaType.APPLICATION_JSON)) - .andExpect(jsonPath("$.message").value(ErrorMessage.INVALID_COUPON_PERIOD.getMessage())); - } - @WithMember @DisplayName("GET - 나의 쿠폰함에서 특정 쿠폰을 조회한다. - List") @Test - void getWallet_success() throws Exception { + void getAllByWalletIdAndMemberId_success() throws Exception { // Given Coupon coupon = couponRepository.save(CouponFixture.coupon()); - couponWalletRepository.save(CouponWallet.create(1L, coupon)); + CouponWallet couponWallet = couponWalletRepository.save(CouponWallet.create(1L, coupon)); // When & Then - mockMvc.perform(get("/my-coupons/" + coupon.getId())) + mockMvc.perform(get("/my-coupons/" + couponWallet.getId())) .andDo(print()) .andDo(document("my-coupons/couponId", preprocessRequest(prettyPrint()), @@ -416,7 +327,7 @@ void getWallet_success() throws Exception { @DisplayName("GET - 나의 쿠폰 보관함에 있는 모든 쿠폰을 조회한다. - List") @MethodSource("com.moabam.support.fixture.CouponWalletFixture#provideCouponWalletByCouponId1_total5") @ParameterizedTest - void getWallet_all_success(List couponWallets) throws Exception { + void getAllByWalletIdAndMemberId_all_success(List couponWallets) throws Exception { // Given couponWallets.forEach(couponWallet -> { Coupon coupon = couponRepository.save(couponWallet.getCoupon()); @@ -438,7 +349,7 @@ void getWallet_all_success(List couponWallets) throws Exception { @WithMember @DisplayName("GET - 쿠폰이 없는 사용자의 쿠폰함을 조회한다. - List") @Test - void getWallet_no_coupon() throws Exception { + void getAllByWalletIdAndMemberId_no_coupon() throws Exception { // When & Then mockMvc.perform(get("/my-coupons")) .andDo(print()) @@ -504,4 +415,93 @@ void use_BadRequestException() throws Exception { .andExpect(content().contentType(MediaType.APPLICATION_JSON)) .andExpect(jsonPath("$.message").value(ErrorMessage.INVALID_DISCOUNT_COUPON.getMessage())); } + + @Disabled + @WithMember + @DisplayName("POST - 쿠폰 발급 요청을 성공적으로 한다. - Void") + @Test + void registerQueue_success() throws Exception { + // Given + Coupon couponFixture = CouponFixture.coupon(); + Coupon coupon = couponRepository.save(couponFixture); + + given(clockHolder.date()).willReturn(LocalDate.of(2023, 2, 1)); + + // When & Then + mockMvc.perform(post("/coupons") + .param("couponName", coupon.getName())) + .andDo(print()) + .andDo(document("coupons", + preprocessRequest(prettyPrint()), + preprocessResponse(prettyPrint()))) + .andExpect(status().isOk()); + } + + @WithMember + @DisplayName("POST - 발급 가능 날짜가 아닌 쿠폰에 발급 요청을 한다. (Not found) - BadRequestException") + @Test + void registerQueue_Zero_StartAt_BadRequestException() throws Exception { + // Given + Coupon couponFixture = CouponFixture.coupon(); + Coupon coupon = couponRepository.save(couponFixture); + + given(clockHolder.date()).willReturn(LocalDate.of(2022, 1, 1)); + + // When & Then + mockMvc.perform(post("/coupons") + .param("couponName", coupon.getName())) + .andDo(print()) + .andDo(document("coupons", + preprocessRequest(prettyPrint()), + preprocessResponse(prettyPrint()), + ErrorSnippet.ERROR_MESSAGE_RESPONSE)) + .andExpect(status().isBadRequest()) + .andExpect(content().contentType(MediaType.APPLICATION_JSON)) + .andExpect(jsonPath("$.message").value(ErrorMessage.INVALID_COUPON_PERIOD.getMessage())); + } + + @WithMember + @DisplayName("POST - 발급 가능 날짜가 아닌 쿠폰에 발급 요청을 한다. (Not equals) - BadRequestException") + @Test + void registerQueue_Not_StartAt_BadRequestException() throws Exception { + // Given + Coupon couponFixture = CouponFixture.coupon(); + couponRepository.save(couponFixture); + + given(clockHolder.date()).willReturn(LocalDate.of(2022, 2, 1)); + + // When & Then + mockMvc.perform(post("/coupons") + .param("couponName", "not start couponName")) + .andDo(print()) + .andDo(document("coupons", + preprocessRequest(prettyPrint()), + preprocessResponse(prettyPrint()), + ErrorSnippet.ERROR_MESSAGE_RESPONSE)) + .andExpect(status().isBadRequest()) + .andExpect(content().contentType(MediaType.APPLICATION_JSON)) + .andExpect(jsonPath("$.message").value(ErrorMessage.INVALID_COUPON_PERIOD.getMessage())); + } + + @WithMember + @DisplayName("POST - 존재하지 않는 쿠폰에 발급 요청을 한다. - NotFoundException") + @Test + void registerQueue_NotFoundException() throws Exception { + // Given + Coupon coupon = CouponFixture.coupon("Not found couponName", 2, 1); + + given(clockHolder.date()).willReturn(LocalDate.of(2023, 2, 1)); + + // When & Then + mockMvc.perform(post("/coupons") + .param("couponName", coupon.getName())) + .andDo(print()) + .andDo(document("coupons", + preprocessRequest(prettyPrint()), + preprocessResponse(prettyPrint()), + ErrorSnippet.ERROR_MESSAGE_RESPONSE)) + .andExpect(status().isBadRequest()) + .andExpect(content().contentType(MediaType.APPLICATION_JSON)) + .andExpect(jsonPath("$.message").value(ErrorMessage.INVALID_COUPON_PERIOD.getMessage())); + } } diff --git a/src/test/java/com/moabam/support/snippet/CouponWalletSnippet.java b/src/test/java/com/moabam/support/snippet/CouponWalletSnippet.java index f6d260be..8e1b0c1e 100644 --- a/src/test/java/com/moabam/support/snippet/CouponWalletSnippet.java +++ b/src/test/java/com/moabam/support/snippet/CouponWalletSnippet.java @@ -8,6 +8,7 @@ public final class CouponWalletSnippet { public static final ResponseFieldsSnippet COUPON_WALLET_RESPONSE = responseFields( + fieldWithPath("[].walletId").type(NUMBER).description("쿠폰지갑 ID"), fieldWithPath("[].id").type(NUMBER).description("쿠폰 ID"), fieldWithPath("[].name").type(STRING).description("쿠폰명"), fieldWithPath("[].description").type(STRING).description("쿠폰에 대한 간단 소개 (NULL 가능)"), From 4340f13ac933da7d2cba1587d543ac5c21158258 Mon Sep 17 00:00:00 2001 From: HyuckJuneHong Date: Wed, 29 Nov 2023 19:12:05 +0900 Subject: [PATCH 3/5] =?UTF-8?q?refactor:=20=EC=BF=A0=ED=8F=B0=20=EB=93=B1?= =?UTF-8?q?=EB=A1=9D,=20=EC=82=AC=EC=9A=A9=20=EC=BD=94=EB=93=9C=20?= =?UTF-8?q?=EA=B0=9C=EC=84=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../coupon/CouponManageService.java | 2 +- .../api/application/coupon/CouponService.java | 8 +- .../api/presentation/CouponController.java | 8 +- src/main/resources/static/docs/coupon.html | 2909 ++++------------- .../coupon/CouponManageServiceTest.java | 4 +- .../presentation/CouponControllerTest.java | 7 +- 6 files changed, 622 insertions(+), 2316 deletions(-) diff --git a/src/main/java/com/moabam/api/application/coupon/CouponManageService.java b/src/main/java/com/moabam/api/application/coupon/CouponManageService.java index 4d46171a..09be5dc1 100644 --- a/src/main/java/com/moabam/api/application/coupon/CouponManageService.java +++ b/src/main/java/com/moabam/api/application/coupon/CouponManageService.java @@ -73,7 +73,7 @@ public void issue() { } } - public void registerQueue(Long memberId, String couponName) { + public void registerQueue(String couponName, Long memberId) { double registerTime = System.currentTimeMillis(); validateRegisterQueue(couponName); couponManageRepository.addIfAbsentQueue(couponName, memberId, registerTime); diff --git a/src/main/java/com/moabam/api/application/coupon/CouponService.java b/src/main/java/com/moabam/api/application/coupon/CouponService.java index 17ccb5a3..43b289fb 100644 --- a/src/main/java/com/moabam/api/application/coupon/CouponService.java +++ b/src/main/java/com/moabam/api/application/coupon/CouponService.java @@ -64,18 +64,22 @@ public void delete(Long couponId, Role role) { } @Transactional - public void use(Long memberId, Long couponWalletId) { + public void use(Long couponWalletId, Long memberId) { CouponWallet couponWallet = couponWalletSearchRepository.findByIdAndMemberId(couponWalletId, memberId) .orElseThrow(() -> new NotFoundException(ErrorMessage.NOT_FOUND_COUPON_WALLET)); Coupon coupon = couponWallet.getCoupon(); BugType bugType = coupon.getType().getBugType(); + if (coupon.getType().isDiscount()) { + throw new BadRequestException(ErrorMessage.INVALID_DISCOUNT_COUPON); + } + bugService.applyCoupon(memberId, bugType, coupon.getPoint()); couponWalletRepository.delete(couponWallet); } @Transactional - public void discount(Long memberId, Long couponWalletId) { + public void discount(Long couponWalletId, Long memberId) { CouponWallet couponWallet = couponWalletSearchRepository.findByIdAndMemberId(couponWalletId, memberId) .orElseThrow(() -> new NotFoundException(ErrorMessage.NOT_FOUND_COUPON_WALLET)); Coupon coupon = couponWallet.getCoupon(); diff --git a/src/main/java/com/moabam/api/presentation/CouponController.java b/src/main/java/com/moabam/api/presentation/CouponController.java index d3909d65..01931878 100644 --- a/src/main/java/com/moabam/api/presentation/CouponController.java +++ b/src/main/java/com/moabam/api/presentation/CouponController.java @@ -66,13 +66,13 @@ public List getAllByWalletIdAndMemberId( @PostMapping("/my-coupons/{couponWalletId}") @ResponseStatus(HttpStatus.OK) - public void use(@Auth AuthMember authMember, @PathVariable("couponWalletId") Long couponWalletId) { - couponService.use(authMember.id(), couponWalletId); + public void use(@PathVariable("couponWalletId") Long couponWalletId, @Auth AuthMember authMember) { + couponService.use(couponWalletId, authMember.id()); } @PostMapping("/coupons") @ResponseStatus(HttpStatus.OK) - public void registerQueue(@Auth AuthMember authMember, @RequestParam("couponName") String couponName) { - couponManageService.registerQueue(authMember.id(), couponName); + public void registerQueue(@RequestParam("couponName") String couponName, @Auth AuthMember authMember) { + couponManageService.registerQueue(couponName, authMember.id()); } } diff --git a/src/main/resources/static/docs/coupon.html b/src/main/resources/static/docs/coupon.html index d2b00d0d..14237f4b 100644 --- a/src/main/resources/static/docs/coupon.html +++ b/src/main/resources/static/docs/coupon.html @@ -1,2138 +1,467 @@ - - - - - 쿠폰(Coupon) - - + + + + +쿠폰(Coupon) + +
-
-

쿠폰(Coupon)

-
-
-
-
쿠폰에 대해 생성/삭제/조회/발급/사용 기능을 제공합니다.
-
-
-
-
-

쿠폰 생성

-
-
-
관리자가 쿠폰을 생성합니다.
-
-
-

요청

-
-
+
+

쿠폰(Coupon)

+
+
+
+
쿠폰에 대해 생성/삭제/조회/발급/사용 기능을 제공합니다.
+
+
+
+
+

쿠폰 생성

+
+
+
관리자가 쿠폰을 생성합니다.
+
+
+

요청

+
+
POST /admins/coupons HTTP/1.1
 Content-Type: application/json;charset=UTF-8
-Content-Length: 183
+Content-Length: 175
 Host: localhost:8080
 
 {
@@ -2144,66 +473,66 @@ 

요청

"startAt" : "2023-02-01", "openAt" : "2023-01-01" }
-
-
-

응답

-
-
+
+
+

응답

+
+
HTTP/1.1 201 Created
 Access-Control-Allow-Origin:
 Access-Control-Allow-Methods: GET, HEAD, POST, PUT, DELETE, TRACE, OPTIONS, PATCH
 Access-Control-Allow-Headers: Origin, Accept, Access-Control-Request-Method, Access-Control-Request-Headers, X-Requested-With,Content-Type, Referer
 Access-Control-Allow-Credentials: true
 Access-Control-Max-Age: 3600
-
-
-
-
-
-

쿠폰 삭제

-
-
-
관리자가 쿠폰 ID와 일치하는 쿠폰을 삭제합니다.
-
-
-

요청

-
-
-
DELETE /admins/coupons/34 HTTP/1.1
+
+
+
+
+
+

쿠폰 삭제

+
+
+
관리자가 쿠폰 ID와 일치하는 쿠폰을 삭제합니다.
+
+
+

요청

+
+
+
DELETE /admins/coupons/35 HTTP/1.1
 Host: localhost:8080
-
-
-

응답

-
-
+
+
+

응답

+
+
HTTP/1.1 200 OK
 Access-Control-Allow-Origin:
 Access-Control-Allow-Methods: GET, HEAD, POST, PUT, DELETE, TRACE, OPTIONS, PATCH
 Access-Control-Allow-Headers: Origin, Accept, Access-Control-Request-Method, Access-Control-Request-Headers, X-Requested-With,Content-Type, Referer
 Access-Control-Allow-Credentials: true
 Access-Control-Max-Age: 3600
-
-
-
-
-
-

특정 쿠폰 조회

-
-
-
관리자 혹은 사용자가 특정 ID와 일치하는 쿠폰을 조회합니다.
-
-
-
-

요청

-
-
-
GET /coupons/22 HTTP/1.1
+
+
+
+
+
+

특정 쿠폰 조회

+
+
+
관리자 혹은 사용자가 특정 ID와 일치하는 쿠폰을 조회합니다.
+
+
+
+

요청

+
+
+
GET /coupons/23 HTTP/1.1
 Host: localhost:8080
-
-
-

응답

-
-
+
+
+

응답

+
+
HTTP/1.1 200 OK
 Access-Control-Allow-Origin:
 Access-Control-Allow-Methods: GET, HEAD, POST, PUT, DELETE, TRACE, OPTIONS, PATCH
@@ -2211,10 +540,10 @@ 

응답

Access-Control-Allow-Credentials: true Access-Control-Max-Age: 3600 Content-Type: application/json -Content-Length: 208 +Content-Length: 198 { - "id" : 22, + "id" : 23, "adminName" : "1admin", "name" : "couponName", "description" : "", @@ -2224,36 +553,36 @@

응답

"startAt" : "2023-02-01", "openAt" : "2023-01-01" }
-
-
-
-
-
-
-

상태에 따른 쿠폰들을 조회

-
-
-
관리자 혹은 사용자가 날짜 상태에 따라 쿠폰들을 조회합니다.
-
-
-
-

요청

-
-
+
+
+
+
+
+
+

상태에 따른 쿠폰들을 조회

+
+
+
관리자 혹은 사용자가 날짜 상태에 따라 쿠폰들을 조회합니다.
+
+
+
+

요청

+
+
POST /coupons/search HTTP/1.1
 Content-Type: application/json;charset=UTF-8
-Content-Length: 44
+Content-Length: 41
 Host: localhost:8080
 
 {
   "opened" : false,
   "ended" : false
 }
-
-
-

응답

-
-
+
+
+

응답

+
+
HTTP/1.1 200 OK
 Access-Control-Allow-Origin:
 Access-Control-Allow-Methods: GET, HEAD, POST, PUT, DELETE, TRACE, OPTIONS, PATCH
@@ -2261,10 +590,10 @@ 

응답

Access-Control-Allow-Credentials: true Access-Control-Max-Age: 3600 Content-Type: application/json -Content-Length: 209 +Content-Length: 199 [ { - "id" : 23, + "id" : 24, "adminName" : "1admin", "name" : "coupon1", "description" : "", @@ -2274,33 +603,33 @@

응답

"startAt" : "2023-03-01", "openAt" : "2023-01-01" } ]
-
-
-
-
-
-
-

특정 쿠폰에 대해 발급

-
-
-
사용자가 발급 가능한 쿠폰을 선착순으로 발급 받습니다.
-
-
-
-

요청

-
-
+
+
+
+
+
+
+

특정 쿠폰에 대해 발급

+
+
+
사용자가 발급 가능한 쿠폰을 선착순으로 발급 받습니다.
+
+
+
+

요청

+
+
POST /coupons HTTP/1.1
 Content-Type: application/x-www-form-urlencoded
 Host: localhost:8080
 Content-Length: 21
 
 couponName=couponName
-
-
-

응답

-
-
+
+
+

응답

+
+
HTTP/1.1 400 Bad Request
 Access-Control-Allow-Origin:
 Access-Control-Allow-Methods: GET, HEAD, POST, PUT, DELETE, TRACE, OPTIONS, PATCH
@@ -2308,34 +637,34 @@ 

응답

Access-Control-Allow-Credentials: true Access-Control-Max-Age: 3600 Content-Type: application/json -Content-Length: 66 +Content-Length: 64 { "message" : "쿠폰 발급 가능 기간이 아닙니다." }
-
-
-
-
-
-
-

특정 사용자의 쿠폰 보관함을 조회

-
-
-
사용자가 자신의 보관함에 있는 쿠폰들을 조회합니다.
-
-
-
-

요청

-
-
+
+
+
+
+
+
+

특정 사용자의 쿠폰 보관함을 조회

+
+
+
사용자가 자신의 보관함에 있는 쿠폰들을 조회합니다.
+
+
+
+

요청

+
+
GET /my-coupons HTTP/1.1
 Host: localhost:8080
-
-
-

응답

-
-
+
+
+

응답

+
+
HTTP/1.1 200 OK
 Access-Control-Allow-Origin:
 Access-Control-Allow-Methods: GET, HEAD, POST, PUT, DELETE, TRACE, OPTIONS, PATCH
@@ -2343,82 +672,52 @@ 

응답

Access-Control-Allow-Credentials: true Access-Control-Max-Age: 3600 Content-Type: application/json -Content-Length: 502 +Content-Length: 3 -[ { - "id" : 17, - "name" : "c1", - "description" : "", - "point" : 10, - "type" : "MORNING" -}, { - "id" : 18, - "name" : "c2", - "description" : "", - "point" : 10, - "type" : "MORNING" -}, { - "id" : 19, - "name" : "c3", - "description" : "", - "point" : 10, - "type" : "MORNING" -}, { - "id" : 20, - "name" : "c4", - "description" : "", - "point" : 10, - "type" : "MORNING" -}, { - "id" : 21, - "name" : "c5", - "description" : "", - "point" : 10, - "type" : "MORNING" -} ]
-
-
-
-
-
-
-

쿠폰을 사용

-
-
-
사용자가 자신의 보관함에 있는 쿠폰들을 사용합니다.
-
-
-
-

요청

-
-
+[ ] +
+
+
+
+
+
+

쿠폰을 사용

+
+
+
사용자가 자신의 보관함에 있는 쿠폰들을 사용합니다.
+
+
+
+

요청

+
+
POST /my-coupons/8 HTTP/1.1
 Host: localhost:8080
 Content-Type: application/x-www-form-urlencoded
-
-
-

응답

-
-
+
+
+

응답

+
+
HTTP/1.1 200 OK
 Access-Control-Allow-Origin:
 Access-Control-Allow-Methods: GET, HEAD, POST, PUT, DELETE, TRACE, OPTIONS, PATCH
 Access-Control-Allow-Headers: Origin, Accept, Access-Control-Request-Method, Access-Control-Request-Headers, X-Requested-With,Content-Type, Referer
 Access-Control-Allow-Credentials: true
 Access-Control-Max-Age: 3600
-
-
-
-
-
-
-
+
+
+
+
+
+
+
- + \ No newline at end of file diff --git a/src/test/java/com/moabam/api/application/coupon/CouponManageServiceTest.java b/src/test/java/com/moabam/api/application/coupon/CouponManageServiceTest.java index bc0de6cf..ea6a7b48 100644 --- a/src/test/java/com/moabam/api/application/coupon/CouponManageServiceTest.java +++ b/src/test/java/com/moabam/api/application/coupon/CouponManageServiceTest.java @@ -129,7 +129,7 @@ void registerQueue_success() { given(couponRepository.existsByNameAndStartAt(any(String.class), any(LocalDate.class))).willReturn(true); // When - couponManageService.registerQueue(1L, coupon.getName()); + couponManageService.registerQueue(coupon.getName(), 1L); // Then verify(couponManageRepository).addIfAbsentQueue(any(String.class), any(Long.class), any(double.class)); @@ -143,7 +143,7 @@ void registerQueue_No_BadRequestException() { given(couponRepository.existsByNameAndStartAt(any(String.class), any(LocalDate.class))).willReturn(false); // When & Then - assertThatThrownBy(() -> couponManageService.registerQueue(1L, "couponName")) + assertThatThrownBy(() -> couponManageService.registerQueue("couponName", 1L)) .isInstanceOf(BadRequestException.class) .hasMessage(ErrorMessage.INVALID_COUPON_PERIOD.getMessage()); } diff --git a/src/test/java/com/moabam/api/presentation/CouponControllerTest.java b/src/test/java/com/moabam/api/presentation/CouponControllerTest.java index 12d167d9..757f79c3 100644 --- a/src/test/java/com/moabam/api/presentation/CouponControllerTest.java +++ b/src/test/java/com/moabam/api/presentation/CouponControllerTest.java @@ -11,7 +11,6 @@ import java.time.LocalDate; import java.util.List; -import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; import org.junit.jupiter.params.ParameterizedTest; @@ -36,6 +35,7 @@ import com.moabam.api.domain.member.repository.MemberRepository; import com.moabam.api.dto.coupon.CouponStatusRequest; import com.moabam.api.dto.coupon.CreateCouponRequest; +import com.moabam.api.infrastructure.redis.ZSetRedisRepository; import com.moabam.global.common.util.ClockHolder; import com.moabam.global.error.model.ErrorMessage; import com.moabam.support.annotation.WithMember; @@ -70,6 +70,9 @@ class CouponControllerTest extends WithoutFilterSupporter { @MockBean ClockHolder clockHolder; + @MockBean + ZSetRedisRepository zSetRedisRepository; + @WithMember(role = Role.ADMIN) @DisplayName("POST - 쿠폰을 성공적으로 발행한다. - Void") @Test @@ -416,7 +419,6 @@ void use_BadRequestException() throws Exception { .andExpect(jsonPath("$.message").value(ErrorMessage.INVALID_DISCOUNT_COUPON.getMessage())); } - @Disabled @WithMember @DisplayName("POST - 쿠폰 발급 요청을 성공적으로 한다. - Void") @Test @@ -426,6 +428,7 @@ void registerQueue_success() throws Exception { Coupon coupon = couponRepository.save(couponFixture); given(clockHolder.date()).willReturn(LocalDate.of(2023, 2, 1)); + willDoNothing().given(zSetRedisRepository).addIfAbsent(anyString(), anyLong(), anyDouble(), anyInt()); // When & Then mockMvc.perform(post("/coupons") From e83e7402706569894e324642e860bafbee832f40 Mon Sep 17 00:00:00 2001 From: HyuckJuneHong Date: Wed, 29 Nov 2023 19:33:25 +0900 Subject: [PATCH 4/5] =?UTF-8?q?refactor:=20FCM=20=EB=B0=8F=20=EC=95=8C?= =?UTF-8?q?=EB=A6=BC=20=EC=BD=94=EB=93=9C=20=EA=B0=9C=EC=84=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../notification/NotificationService.java | 15 +++++------ .../application/payment/PaymentService.java | 2 +- .../repository/NotificationRepository.java | 20 +++++++++----- .../api/infrastructure/fcm/FcmRepository.java | 13 ++++----- .../api/infrastructure/fcm/FcmService.java | 7 ++--- .../presentation/NotificationController.java | 13 +++++---- src/main/resources/static/docs/index.html | 2 +- .../resources/static/docs/notification.html | 2 +- .../notification/NotificationServiceTest.java | 20 +++----------- .../payment/PaymentServiceTest.java | 2 +- .../infrastructure/fcm/FcmRepositoryTest.java | 26 +++--------------- .../infrastructure/fcm/FcmServiceTest.java | 27 +++++-------------- .../NotificationControllerTest.java | 4 +-- 13 files changed, 55 insertions(+), 98 deletions(-) diff --git a/src/main/java/com/moabam/api/application/notification/NotificationService.java b/src/main/java/com/moabam/api/application/notification/NotificationService.java index 7eedd953..3296fce5 100644 --- a/src/main/java/com/moabam/api/application/notification/NotificationService.java +++ b/src/main/java/com/moabam/api/application/notification/NotificationService.java @@ -16,7 +16,6 @@ import com.moabam.api.domain.room.Participant; import com.moabam.api.domain.room.repository.ParticipantSearchRepository; import com.moabam.api.infrastructure.fcm.FcmService; -import com.moabam.global.auth.model.AuthMember; import com.moabam.global.common.util.ClockHolder; import com.moabam.global.error.exception.ConflictException; import com.moabam.global.error.exception.NotFoundException; @@ -40,14 +39,14 @@ public class NotificationService { private final ParticipantSearchRepository participantSearchRepository; @Transactional - public void sendKnock(AuthMember member, Long targetId, Long roomId) { + public void sendKnock(Long roomId, Long targetId, Long memberId, String memberNickname) { roomService.validateRoomById(roomId); - validateConflictKnock(member.id(), targetId, roomId); + validateConflictKnock(roomId, targetId, memberId); String fcmToken = fcmService.findTokenByMemberId(targetId) .orElseThrow(() -> new NotFoundException(ErrorMessage.NOT_FOUND_FCM_TOKEN)); - fcmService.sendAsync(fcmToken, String.format(KNOCK_BODY, member.nickname())); - notificationRepository.saveKnock(member.id(), targetId, roomId); + fcmService.sendAsync(fcmToken, String.format(KNOCK_BODY, memberNickname)); + notificationRepository.saveKnock(roomId, targetId, memberId); } public void sendCouponIssueResult(Long memberId, String couponName, String body) { @@ -74,7 +73,7 @@ public List getMyKnockStatusInRoom(Long memberId, Long roomId, List knockPredicate = targetId -> - notificationRepository.existsKnockByKey(memberId, targetId, roomId); + notificationRepository.existsKnockByKey(roomId, targetId, memberId); Map> knockStatus = filteredParticipants.stream() .map(Participant::getMemberId) @@ -83,8 +82,8 @@ public List getMyKnockStatusInRoom(Long memberId, Long roomId, List diff --git a/src/main/resources/static/docs/notification.html b/src/main/resources/static/docs/notification.html index 6f789844..d56f528e 100644 --- a/src/main/resources/static/docs/notification.html +++ b/src/main/resources/static/docs/notification.html @@ -513,7 +513,7 @@

응답

diff --git a/src/test/java/com/moabam/api/application/notification/NotificationServiceTest.java b/src/test/java/com/moabam/api/application/notification/NotificationServiceTest.java index 9b411f8a..2a49ab6f 100644 --- a/src/test/java/com/moabam/api/application/notification/NotificationServiceTest.java +++ b/src/test/java/com/moabam/api/application/notification/NotificationServiceTest.java @@ -53,71 +53,59 @@ class NotificationServiceTest { String successIssueResult = "%s 쿠폰 발행을 성공했습니다. 축하드립니다!"; - @WithMember @DisplayName("상대에게 콕 알림을 성공적으로 보낸다. - Void") @Test void sendKnock_success() { // Given - AuthMember member = AuthorizationThreadLocal.getAuthMember(); - willDoNothing().given(roomService).validateRoomById(any(Long.class)); given(fcmService.findTokenByMemberId(any(Long.class))).willReturn(Optional.of("FCM-TOKEN")); given(notificationRepository.existsKnockByKey(any(Long.class), any(Long.class), any(Long.class))) .willReturn(false); // When - notificationService.sendKnock(member, 2L, 1L); + notificationService.sendKnock(1L, 1L, 2L, "nickName"); // Then verify(fcmService).sendAsync(any(String.class), any(String.class)); verify(notificationRepository).saveKnock(any(Long.class), any(Long.class), any(Long.class)); } - @WithMember @DisplayName("콕 찌를 상대의 방이 존재하지 않는다. - NotFoundException") @Test void sendKnock_Room_NotFoundException() { // Given - AuthMember member = AuthorizationThreadLocal.getAuthMember(); - willThrow(NotFoundException.class).given(roomService).validateRoomById(any(Long.class)); // When & Then - assertThatThrownBy(() -> notificationService.sendKnock(member, 1L, 1L)) + assertThatThrownBy(() -> notificationService.sendKnock(1L, 1L, 2L, "nickName")) .isInstanceOf(NotFoundException.class); } - @WithMember @DisplayName("콕 찌를 상대의 FCM 토큰이 존재하지 않는다. - NotFoundException") @Test void sendKnock_FcmToken_NotFoundException() { // Given - AuthMember member = AuthorizationThreadLocal.getAuthMember(); - willDoNothing().given(roomService).validateRoomById(any(Long.class)); given(fcmService.findTokenByMemberId(any(Long.class))).willReturn(Optional.empty()); given(notificationRepository.existsKnockByKey(any(Long.class), any(Long.class), any(Long.class))) .willReturn(false); // When & Then - assertThatThrownBy(() -> notificationService.sendKnock(member, 1L, 1L)) + assertThatThrownBy(() -> notificationService.sendKnock(1L, 1L, 2L, "nickName")) .isInstanceOf(NotFoundException.class) .hasMessage(ErrorMessage.NOT_FOUND_FCM_TOKEN.getMessage()); } - @WithMember @DisplayName("콕 찌를 상대가 이미 찌른 상대이다. - ConflictException") @Test void sendKnock_ConflictException() { // Given - AuthMember member = AuthorizationThreadLocal.getAuthMember(); - willDoNothing().given(roomService).validateRoomById(any(Long.class)); given(notificationRepository.existsKnockByKey(any(Long.class), any(Long.class), any(Long.class))) .willReturn(true); // When & Then - assertThatThrownBy(() -> notificationService.sendKnock(member, 1L, 1L)) + assertThatThrownBy(() -> notificationService.sendKnock(1L, 1L, 2L, "nickName")) .isInstanceOf(ConflictException.class) .hasMessage(ErrorMessage.CONFLICT_KNOCK.getMessage()); } diff --git a/src/test/java/com/moabam/api/application/payment/PaymentServiceTest.java b/src/test/java/com/moabam/api/application/payment/PaymentServiceTest.java index 0f992e1d..039563e2 100644 --- a/src/test/java/com/moabam/api/application/payment/PaymentServiceTest.java +++ b/src/test/java/com/moabam/api/application/payment/PaymentServiceTest.java @@ -102,7 +102,7 @@ void use_coupon_success() { paymentService.confirm(memberId, request); // then - verify(couponService, times(1)).discount(memberId, couponWalletId); + verify(couponService, times(1)).discount(couponWalletId, memberId); verify(bugService, times(1)).charge(memberId, payment.getProduct()); } diff --git a/src/test/java/com/moabam/api/infrastructure/fcm/FcmRepositoryTest.java b/src/test/java/com/moabam/api/infrastructure/fcm/FcmRepositoryTest.java index 83f2e887..6eed5267 100644 --- a/src/test/java/com/moabam/api/infrastructure/fcm/FcmRepositoryTest.java +++ b/src/test/java/com/moabam/api/infrastructure/fcm/FcmRepositoryTest.java @@ -28,7 +28,7 @@ class FcmRepositoryTest { @Test void saveToken_success() { // When - fcmRepository.saveToken(1L, "value1"); + fcmRepository.saveToken("FCM-TOKEN", 1L); // Then verify(valueRedisRepository).save(any(String.class), any(String.class), any(Duration.class)); @@ -38,7 +38,7 @@ void saveToken_success() { @Test void saveToken_MemberId_NullPointerException() { // When & Then - assertThatThrownBy(() -> fcmRepository.saveToken(null, "value")) + assertThatThrownBy(() -> fcmRepository.saveToken("FCM-TOKEN", null)) .isInstanceOf(NullPointerException.class); } @@ -46,7 +46,7 @@ void saveToken_MemberId_NullPointerException() { @Test void saveToken_FcmToken_NullPointerException() { // When & Then - assertThatThrownBy(() -> fcmRepository.saveToken(1L, null)) + assertThatThrownBy(() -> fcmRepository.saveToken(null, 1L)) .isInstanceOf(NullPointerException.class); } @@ -60,7 +60,7 @@ void deleteTokenByMemberId_success() { verify(valueRedisRepository).delete(any(String.class)); } - @DisplayName("ID가 Null인 사용자가 FCM 토큰을 삭제한다.. - NullPointerException") + @DisplayName("ID가 Null인 사용자가 FCM 토큰을 삭제한다. - NullPointerException") @Test void deleteTokenByMemberId_NullPointerException() { // When & Then @@ -85,22 +85,4 @@ void findTokenByMemberId_NullPointerException() { assertThatThrownBy(() -> fcmRepository.findTokenByMemberId(null)) .isInstanceOf(NullPointerException.class); } - - @DisplayName("FCM 토큰 존재 여부를 성공적으로 확인한다. - Boolean") - @Test - void existsTokenByMemberId_success() { - // When - fcmRepository.existsTokenByMemberId(1L); - - // Then - verify(valueRedisRepository).hasKey(any(String.class)); - } - - @DisplayName("ID가 Null인 사용자가 FCM 토큰 존재 여부를 확인한다. - NullPointerException") - @Test - void existsTokenByMemberId_NullPointerException() { - // When & Then - assertThatThrownBy(() -> fcmRepository.existsTokenByMemberId(null)) - .isInstanceOf(NullPointerException.class); - } } diff --git a/src/test/java/com/moabam/api/infrastructure/fcm/FcmServiceTest.java b/src/test/java/com/moabam/api/infrastructure/fcm/FcmServiceTest.java index c4b7cc09..942cf849 100644 --- a/src/test/java/com/moabam/api/infrastructure/fcm/FcmServiceTest.java +++ b/src/test/java/com/moabam/api/infrastructure/fcm/FcmServiceTest.java @@ -10,10 +10,7 @@ import com.google.firebase.messaging.FirebaseMessaging; import com.google.firebase.messaging.Message; -import com.moabam.global.auth.model.AuthMember; -import com.moabam.global.auth.model.AuthorizationThreadLocal; import com.moabam.global.config.FcmConfig; -import com.moabam.support.annotation.WithMember; import com.moabam.support.common.WithoutFilterSupporter; @SpringBootTest(classes = {FcmConfig.class, FcmService.class}) @@ -28,46 +25,34 @@ class FcmServiceTest extends WithoutFilterSupporter { @MockBean FcmRepository fcmRepository; - @WithMember @DisplayName("FCM 토큰이 성공적으로 저장된다. - Void") @Test void saveToken_success() { - // Given - AuthMember authMember = AuthorizationThreadLocal.getAuthMember(); - // When - fcmService.createToken(authMember, "value1"); + fcmService.createToken("FCM-TOKEN", 1L); // Then - verify(fcmRepository).saveToken(any(Long.class), any(String.class)); + verify(fcmRepository).saveToken(any(String.class), any(Long.class)); } - @WithMember @DisplayName("FCM 토큰으로 빈값이 넘어와 아무일도 일어나지 않는다. - Void") @Test void saveToken_Blank() { - // Given - AuthMember authMember = AuthorizationThreadLocal.getAuthMember(); - // When - fcmService.createToken(authMember, ""); + fcmService.createToken("", 1L); // Then - verify(fcmRepository, times(0)).saveToken(any(Long.class), any(String.class)); + verify(fcmRepository, times(0)).saveToken(any(String.class), any(Long.class)); } - @WithMember @DisplayName("FCM 토큰으로 null이 넘어와 아무일도 일어나지 않는다. - Void") @Test void saveToken_Null() { - // Given - AuthMember authMember = AuthorizationThreadLocal.getAuthMember(); - // When - fcmService.createToken(authMember, null); + fcmService.createToken(null, 1L); // Then - verify(fcmRepository, times(0)).saveToken(any(Long.class), any(String.class)); + verify(fcmRepository, times(0)).saveToken(any(String.class), any(Long.class)); } @DisplayName("FCM 토큰이 성공적으로 삭제된다. - Void") diff --git a/src/test/java/com/moabam/api/presentation/NotificationControllerTest.java b/src/test/java/com/moabam/api/presentation/NotificationControllerTest.java index 95581ddf..3df0a842 100644 --- a/src/test/java/com/moabam/api/presentation/NotificationControllerTest.java +++ b/src/test/java/com/moabam/api/presentation/NotificationControllerTest.java @@ -121,7 +121,7 @@ void createFcmToken_blank() throws Exception { @Test void sendKnock_success() throws Exception { // Given - fcmRepository.saveToken(target.getId(), "FCM_TOKEN"); + fcmRepository.saveToken("FCM_TOKEN", target.getId()); // When & Then mockMvc.perform(get("/notifications/rooms/" + room.getId() + "/members/" + target.getId())) @@ -153,7 +153,7 @@ void sendKnock_NotFoundException() throws Exception { @Test void sendKnock_ConflictException() throws Exception { // Given - fcmRepository.saveToken(target.getId(), "FCM_TOKEN"); + fcmRepository.saveToken("FCM_TOKEN", target.getId()); notificationRepository.saveKnock(1L, target.getId(), room.getId()); // When & Then From f41285a83458eaa10d13417dbac82d63ade2332e Mon Sep 17 00:00:00 2001 From: HyuckJuneHong Date: Wed, 29 Nov 2023 22:34:43 +0900 Subject: [PATCH 5/5] style: fcm token log --- .../java/com/moabam/api/infrastructure/fcm/FcmService.java | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/main/java/com/moabam/api/infrastructure/fcm/FcmService.java b/src/main/java/com/moabam/api/infrastructure/fcm/FcmService.java index cc575968..6fc15f68 100644 --- a/src/main/java/com/moabam/api/infrastructure/fcm/FcmService.java +++ b/src/main/java/com/moabam/api/infrastructure/fcm/FcmService.java @@ -9,7 +9,9 @@ import com.google.firebase.messaging.Notification; import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +@Slf4j @Service @RequiredArgsConstructor public class FcmService { @@ -22,7 +24,9 @@ public void createToken(String fcmToken, Long memberId) { return; } + log.info("FCM TOKEN before: " + fcmToken); fcmRepository.saveToken(fcmToken, memberId); + log.info("FCM TOKEN after: " + findTokenByMemberId(memberId)); } public void deleteTokenByMemberId(Long memberId) {