From fae3275e04f6a6763a3efafd3b041f75a1dd47cd Mon Sep 17 00:00:00 2001 From: Jinwoo Lee Date: Fri, 20 Oct 2023 11:57:46 +0900 Subject: [PATCH 1/5] feat: implement expire method to giftcard-entity --- .../org/swmaestro/repl/gifthub/giftcard/entity/Giftcard.java | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/main/java/org/swmaestro/repl/gifthub/giftcard/entity/Giftcard.java b/src/main/java/org/swmaestro/repl/gifthub/giftcard/entity/Giftcard.java index 46f86576..7839656e 100644 --- a/src/main/java/org/swmaestro/repl/gifthub/giftcard/entity/Giftcard.java +++ b/src/main/java/org/swmaestro/repl/gifthub/giftcard/entity/Giftcard.java @@ -52,4 +52,8 @@ public Giftcard(String id, Voucher voucher, String message, LocalDateTime expire this.password = password; this.expiresAt = expiresAt; } + + public void expire() { + this.expiresAt = LocalDateTime.now(); + } } From 4110afc3a7dbf181183333756cd8917351e0688e Mon Sep 17 00:00:00 2001 From: Jinwoo Lee Date: Fri, 20 Oct 2023 11:59:10 +0900 Subject: [PATCH 2/5] feat: implement change voucher-owner logic --- .../giftcard/service/GiftcardService.java | 30 ++++++++++++++++++- 1 file changed, 29 insertions(+), 1 deletion(-) diff --git a/src/main/java/org/swmaestro/repl/gifthub/giftcard/service/GiftcardService.java b/src/main/java/org/swmaestro/repl/gifthub/giftcard/service/GiftcardService.java index 3af1cfff..ee372d0a 100644 --- a/src/main/java/org/swmaestro/repl/gifthub/giftcard/service/GiftcardService.java +++ b/src/main/java/org/swmaestro/repl/gifthub/giftcard/service/GiftcardService.java @@ -7,6 +7,8 @@ import org.springframework.security.crypto.encrypt.AesBytesEncryptor; import org.springframework.stereotype.Service; +import org.swmaestro.repl.gifthub.auth.entity.User; +import org.swmaestro.repl.gifthub.auth.service.UserService; import org.swmaestro.repl.gifthub.exception.BusinessException; import org.swmaestro.repl.gifthub.giftcard.config.GiftcardConfig; import org.swmaestro.repl.gifthub.giftcard.dto.GiftcardResponseDto; @@ -16,6 +18,7 @@ import org.swmaestro.repl.gifthub.util.StatusEnum; import org.swmaestro.repl.gifthub.vouchers.dto.VoucherShareResponseDto; import org.swmaestro.repl.gifthub.vouchers.entity.Voucher; +import org.swmaestro.repl.gifthub.vouchers.repository.VoucherRepository; import lombok.RequiredArgsConstructor; @@ -25,6 +28,8 @@ public class GiftcardService { private final GiftcardRepository giftCardRepository; private final GiftcardConfig giftcardConfig; private final AesBytesEncryptor aesBytesEncryptor; + private final UserService userService; + private final VoucherRepository voucherRepository; // 순환 참조로 인해 vocuherService를 사용할 수 없음 public VoucherShareResponseDto create(Voucher voucher, String message) { if (isExist(voucher.getId())) { @@ -47,7 +52,7 @@ public VoucherShareResponseDto create(Voucher voucher, String message) { public Giftcard read(String id) { if (!isExist(id)) { - throw new BusinessException("존재하지 않는 링크입니다.", StatusEnum.NOT_FOUND); + throw new BusinessException("존재하지 않는 기프트 카드입니다.", StatusEnum.NOT_FOUND); } return giftCardRepository.findById(id).get(); @@ -73,6 +78,29 @@ public GiftcardResponseDto read(String id, String password) { .build(); } + /** + * 기프트 카드의 소유자를 변경합니다. + * @param giftcardId: 변경할 기프트 카드의 id + * @param username: 변경할 소유자의 username + * @return + */ + public Giftcard changeVoucherUser(String giftcardId, String username) { + Giftcard giftcard = read(giftcardId); + + if (giftcard.getExpiresAt().isBefore(LocalDateTime.now())) { + throw new BusinessException("만료된 링크입니다.", StatusEnum.BAD_REQUEST); + } + + Voucher updatedVoucher = giftcard.getVoucher(); + User newUser = userService.read(username); + updatedVoucher.setUser(newUser); + voucherRepository.save(updatedVoucher); + + giftcard.expire(); + giftCardRepository.save(giftcard); + return giftcard; + } + public boolean isExist(String id) { return giftCardRepository.existsById(id); } From 81f61b535574bdfbe5ce32206de578f016632e05 Mon Sep 17 00:00:00 2001 From: Jinwoo Lee Date: Fri, 20 Oct 2023 12:02:38 +0900 Subject: [PATCH 3/5] docs: add comment --- .../giftcard/service/GiftcardService.java | 47 ++++++++++++++++++- 1 file changed, 46 insertions(+), 1 deletion(-) diff --git a/src/main/java/org/swmaestro/repl/gifthub/giftcard/service/GiftcardService.java b/src/main/java/org/swmaestro/repl/gifthub/giftcard/service/GiftcardService.java index ee372d0a..8f2b369d 100644 --- a/src/main/java/org/swmaestro/repl/gifthub/giftcard/service/GiftcardService.java +++ b/src/main/java/org/swmaestro/repl/gifthub/giftcard/service/GiftcardService.java @@ -31,6 +31,12 @@ public class GiftcardService { private final UserService userService; private final VoucherRepository voucherRepository; // 순환 참조로 인해 vocuherService를 사용할 수 없음 + /** + * 기프트 카드를 생성합니다. + * @param voucher: 기프트 카드를 생성할 기프티콘 + * @param message: 기프트 카드에 담을 메시지 + * @return: 생성된 기프트 카드의 id + */ public VoucherShareResponseDto create(Voucher voucher, String message) { if (isExist(voucher.getId())) { throw new BusinessException("이미 공유된 기프티콘입니다.", StatusEnum.BAD_REQUEST); @@ -50,6 +56,11 @@ public VoucherShareResponseDto create(Voucher voucher, String message) { .build(); } + /** + * 기프트 카드를 조회합니다. + * @param id: 조회할 기프트 카드의 id + * @return: 조회된 기프트 카드 + */ public Giftcard read(String id) { if (!isExist(id)) { throw new BusinessException("존재하지 않는 기프트 카드입니다.", StatusEnum.NOT_FOUND); @@ -58,6 +69,12 @@ public Giftcard read(String id) { return giftCardRepository.findById(id).get(); } + /** + * 기프트 카드를 조회합니다. + * @param id: 조회할 기프트 카드의 id + * @param password: 조회할 기프트 카드의 비밀번호 + * @return: 조회된 기프트 카드 + */ public GiftcardResponseDto read(String id, String password) { Giftcard giftcard = read(id); @@ -82,7 +99,7 @@ public GiftcardResponseDto read(String id, String password) { * 기프트 카드의 소유자를 변경합니다. * @param giftcardId: 변경할 기프트 카드의 id * @param username: 변경할 소유자의 username - * @return + * @return: 변경된 기프트 카드 */ public Giftcard changeVoucherUser(String giftcardId, String username) { Giftcard giftcard = read(giftcardId); @@ -101,29 +118,57 @@ public Giftcard changeVoucherUser(String giftcardId, String username) { return giftcard; } + /** + * 기프트 카드가 존재하는지 확인합니다. (기프트 카드 id로 확인) + * @param id + * @return + */ public boolean isExist(String id) { return giftCardRepository.existsById(id); } + /** + * 기프트 카드가 존재하는지 확인합니다. (기프트 카드의 voucher id로 확인) + * @param voucherId: 기프트 카드의 id + * @return: 기프트 카드가 존재하는지 여부 + */ public boolean isExist(Long voucherId) { return giftCardRepository.existsByVoucherId(voucherId); } + /** + * UUID를 생성하는 메서드 + * @return: 생성된 UUID + */ public String generateUUID() { return UUID.randomUUID().toString(); } + /** + * 4자리의 숫자로 구성된 비밀번호를 생성 + * @return: 생성된 비밀번호 + */ public String generatePassword() { int random = new Random().nextInt(10000); return String.format("%04d", random); } + /** + * 비밀번호 복호화 메서드 + * @param password: 복호화할 비밀번호 + * @return: 복호화된 비밀번호 + */ private String decryptPassword(String password) { byte[] bytes = ByteArrayUtils.stringToByteArray(password); byte[] decrypt = aesBytesEncryptor.decrypt(bytes); return new String(decrypt, StandardCharsets.UTF_8); } + /** + * 비밀번호 암호화 메서드 + * @param password: 암호화할 비밀번호 + * @return: 암호화된 비밀번호 + */ private String encryptPassword(String password) { byte[] bytes = password.getBytes(StandardCharsets.UTF_8); byte[] encrypt = aesBytesEncryptor.encrypt(bytes); From 7e1b2856fae9f7c2d58296493a94de8b2080bdab Mon Sep 17 00:00:00 2001 From: Jinwoo Lee Date: Fri, 20 Oct 2023 12:03:19 +0900 Subject: [PATCH 4/5] feat: implement change-voucher-user to giftcard api --- .../contorller/GiftcardController.java | 20 +++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/src/main/java/org/swmaestro/repl/gifthub/giftcard/contorller/GiftcardController.java b/src/main/java/org/swmaestro/repl/gifthub/giftcard/contorller/GiftcardController.java index 6fa0736d..4cd885bd 100644 --- a/src/main/java/org/swmaestro/repl/gifthub/giftcard/contorller/GiftcardController.java +++ b/src/main/java/org/swmaestro/repl/gifthub/giftcard/contorller/GiftcardController.java @@ -3,11 +3,13 @@ import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; import org.swmaestro.repl.gifthub.giftcard.dto.GiftcardResponseDto; import org.swmaestro.repl.gifthub.giftcard.service.GiftcardService; import org.swmaestro.repl.gifthub.util.BasicAuthenticationDecoder; +import org.swmaestro.repl.gifthub.util.JwtProvider; import org.swmaestro.repl.gifthub.util.Message; import org.swmaestro.repl.gifthub.util.SuccessMessage; @@ -24,6 +26,7 @@ @Tag(name = "GiftCard", description = "공유하기 관련 API") public class GiftcardController { private final GiftcardService giftcardService; + private final JwtProvider jwtProvider; @GetMapping("/{id}") @Operation(summary = "공유 정보 요청 메서드", description = "클라이언트에서 요청한 공유 정보를 전달하기 위한 메서드입니다.") @@ -41,4 +44,21 @@ public ResponseEntity read(HttpServletRequest request, @PathVariable St .data(giftcardResponseDto) .build()); } + + @PostMapping("/{id}/acquire") + @Operation(summary = "기프티콘 소유자 변경 메서드", description = "클라이언트의 요청을 통해 기프티콘 소유자를 변경하기 위한 메서드입니다.") + @ApiResponses({ + @ApiResponse(responseCode = "200", description = "공유하기 정보 조회 성공"), + @ApiResponse(responseCode = "404", description = "존재하지 않는 공유하기 정보 접근") + }) + public ResponseEntity changeVoucherUser( + HttpServletRequest request, + @PathVariable String id) { + String username = jwtProvider.getUsername(jwtProvider.resolveToken(request).substring(7)); + giftcardService.changeVoucherUser(id, username); + return ResponseEntity.ok( + SuccessMessage.builder() + .path(request.getRequestURI()) + .build()); + } } From 71fda92bb6efea1d871db1681ca68a29311e8b20 Mon Sep 17 00:00:00 2001 From: Jinwoo Lee Date: Fri, 20 Oct 2023 12:12:52 +0900 Subject: [PATCH 5/5] test: write change-voucher-user to giftcard-controller-test --- .../contorller/GiftcardControllerTest.java | 37 +++++++++++++++++++ 1 file changed, 37 insertions(+) diff --git a/src/test/java/org/swmaestro/repl/gifthub/giftcard/contorller/GiftcardControllerTest.java b/src/test/java/org/swmaestro/repl/gifthub/giftcard/contorller/GiftcardControllerTest.java index 74adaa52..120b0501 100644 --- a/src/test/java/org/swmaestro/repl/gifthub/giftcard/contorller/GiftcardControllerTest.java +++ b/src/test/java/org/swmaestro/repl/gifthub/giftcard/contorller/GiftcardControllerTest.java @@ -15,7 +15,10 @@ import org.springframework.security.test.context.support.WithMockUser; import org.springframework.test.web.servlet.MockMvc; import org.swmaestro.repl.gifthub.giftcard.dto.GiftcardResponseDto; +import org.swmaestro.repl.gifthub.giftcard.entity.Giftcard; import org.swmaestro.repl.gifthub.giftcard.service.GiftcardService; +import org.swmaestro.repl.gifthub.util.JwtProvider; +import org.swmaestro.repl.gifthub.vouchers.entity.Voucher; import com.fasterxml.jackson.databind.ObjectMapper; @@ -31,6 +34,9 @@ class GiftcardControllerTest { @MockBean private GiftcardService giftcardService; + @MockBean + private JwtProvider jwtProvider; + @Test @WithMockUser(username = "test", roles = "USER") void read() throws Exception { @@ -64,4 +70,35 @@ void read() throws Exception { .andExpect(jsonPath("$.data.brand_name").value(giftcardResponseDto.getBrandName())) .andExpect(jsonPath("$.data.expires_at").value(giftcardResponseDto.getExpiresAt().toString())); } + + @Test + @WithMockUser(username = "test", roles = "USER") + void changeVoucherUser() throws Exception { + // given + String giftcardId = "id"; + String apiPath = "/giftcards/" + giftcardId + "/acquire"; + + Voucher voucher = Voucher.builder() + .id(1L) + .build(); + + Giftcard giftcard = Giftcard.builder() + .id(giftcardId) + .voucher(voucher) + .password("0000") + .message("메시지") + .expiresAt(LocalDate.now().atStartOfDay()) + .build(); + + // when + when(jwtProvider.resolveToken(any())).thenReturn("my_awesome_access_token"); + when(jwtProvider.getUsername(anyString())).thenReturn("test"); + when(giftcardService.changeVoucherUser(giftcardId, "test")).thenReturn(giftcard); + + // then + mockMvc.perform(post(apiPath) + .contentType(MediaType.APPLICATION_JSON)) + .andExpect(status().isOk()) + .andExpect(jsonPath("$.path").value(apiPath)); + } } \ No newline at end of file