From 8afe9df28a2309eac043d194c180b45df244ca8e Mon Sep 17 00:00:00 2001 From: Sejin Park <95167215+sejineer@users.noreply.github.com> Date: Wed, 6 Mar 2024 03:55:57 +0900 Subject: [PATCH] =?UTF-8?q?feat:=20=EC=98=88=EC=95=BD=20=EB=B3=80=EA=B2=BD?= =?UTF-8?q?=20=EC=95=8C=EB=A6=BC=ED=86=A1=20=EC=A0=81=EC=9A=A9=20(#289)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * feat: 알림톡 기능 구현 / 예약등록 알림톡 적용 * feat: 예약 확정 알림톡 적용 * feat: 예약 확정 + 초대장 알림톡 적용 * feat: 예약 취소 알림톡 구현 * feat: 예약 취소 알림톡 구현 * fix: 예약 확정 로직 수정 * feat: 예약 변경 알림톡 적용 --- .../ReservationManipulationService.java | 2 +- .../ReservationManipulationServiceImpl.java | 179 +++++++++--------- .../dto/request/UpdateReservationReq.java | 2 +- .../presentation/ReservationController.java | 2 +- .../infrastructure/sms/NaverSmsClient.java | 51 +++++ 5 files changed, 147 insertions(+), 89 deletions(-) diff --git a/src/main/java/com/shallwe/domain/reservation/application/ReservationManipulationService.java b/src/main/java/com/shallwe/domain/reservation/application/ReservationManipulationService.java index 1fac3acc..8c69bac4 100644 --- a/src/main/java/com/shallwe/domain/reservation/application/ReservationManipulationService.java +++ b/src/main/java/com/shallwe/domain/reservation/application/ReservationManipulationService.java @@ -12,7 +12,7 @@ public interface ReservationManipulationService { List addOwnerReservation(OwnerReservationCreate ownerReservationCreate, UserPrincipal userPrincipal); ReservationResponse addUserReservation(UserReservationCreate reservationRequest, UserPrincipal userPrincipal) throws Exception; - ReservationResponse updateReservation(UpdateReservationReq updateReq, UserPrincipal userPrincipal); + ReservationResponse updateReservation(UpdateReservationReq updateReq, UserPrincipal userPrincipal) throws Exception; void cancelReservation(UserPrincipal userPrincipal, Long reservationId) throws Exception; } diff --git a/src/main/java/com/shallwe/domain/reservation/application/ReservationManipulationServiceImpl.java b/src/main/java/com/shallwe/domain/reservation/application/ReservationManipulationServiceImpl.java index 4705ff59..7f55e0ef 100644 --- a/src/main/java/com/shallwe/domain/reservation/application/ReservationManipulationServiceImpl.java +++ b/src/main/java/com/shallwe/domain/reservation/application/ReservationManipulationServiceImpl.java @@ -38,91 +38,98 @@ @Transactional(readOnly = true) public class ReservationManipulationServiceImpl implements ReservationManipulationService { - private final ReservationRepository reservationRepository; - private final ExperienceGiftRepository experienceGiftRepository; - private final UserRepository userRepository; - private final NaverSmsClient naverSmsClient; - - @Transactional - public List addOwnerReservation( - OwnerReservationCreate ownerReservationCreate, UserPrincipal userPrincipal) { - ExperienceGift experienceGift = experienceGiftRepository.findById( - ownerReservationCreate.getExperienceGiftId()) - .orElseThrow(ExperienceGiftNotFoundException::new); - - List reservations = OwnerReservationCreate.toEntityForOwner(ownerReservationCreate, - experienceGift); - return reservations.stream() - .map(reservationRepository::save) - .map(ReservationResponse::toDtoOwner) - .collect(Collectors.toList()); - } - - @Transactional - public ReservationResponse addUserReservation(UserReservationCreate reservationRequest, - UserPrincipal userPrincipal) throws Exception { - User sender = userRepository.findById(userPrincipal.getId()) - .orElseThrow(InvalidSenderException::new); - - User receiver = userRepository.findByPhoneNumberAndStatus(reservationRequest.getPhoneNumber(), - Status.ACTIVE) - .orElseThrow(InvalidReceiverException::new); - - ExperienceGift experienceGift = experienceGiftRepository.findExperienceGiftById( - reservationRequest.getExperienceGiftId()) - .orElseThrow(ExperienceGiftNotFoundException::new); - - Reservation reservation = reservationRepository.findByDateAndTimeAndExperienceGiftWithPessimisticLock( - reservationRequest.getDate(), reservationRequest.getTime(), experienceGift) - .orElseThrow(InvalidReservationException::new); - - if (reservation.getReservationStatus().equals(WAITING)) { - reservation.updateStatus(BOOKED); - reservation.updateUserReservationRequest(reservationRequest, sender, receiver); - naverSmsClient.sendApply(receiver, experienceGift, reservation); - experienceGift.addReservationCount(); - } else { - throw new InvalidReservationException(); + private final ReservationRepository reservationRepository; + private final ExperienceGiftRepository experienceGiftRepository; + private final UserRepository userRepository; + private final NaverSmsClient naverSmsClient; + + @Override + @Transactional + public List addOwnerReservation( + OwnerReservationCreate ownerReservationCreate, UserPrincipal userPrincipal) { + ExperienceGift experienceGift = experienceGiftRepository.findById( + ownerReservationCreate.getExperienceGiftId()) + .orElseThrow(ExperienceGiftNotFoundException::new); + + List reservations = OwnerReservationCreate.toEntityForOwner(ownerReservationCreate, + experienceGift); + return reservations.stream() + .map(reservationRepository::save) + .map(ReservationResponse::toDtoOwner) + .collect(Collectors.toList()); + } + + @Override + @Transactional + public ReservationResponse addUserReservation(UserReservationCreate reservationRequest, + UserPrincipal userPrincipal) throws Exception { + User sender = userRepository.findById(userPrincipal.getId()) + .orElseThrow(InvalidSenderException::new); + + User receiver = userRepository.findByPhoneNumberAndStatus(reservationRequest.getPhoneNumber(), + Status.ACTIVE) + .orElseThrow(InvalidReceiverException::new); + + ExperienceGift experienceGift = experienceGiftRepository.findExperienceGiftById( + reservationRequest.getExperienceGiftId()) + .orElseThrow(ExperienceGiftNotFoundException::new); + + Reservation reservation = reservationRepository.findByDateAndTimeAndExperienceGiftWithPessimisticLock( + reservationRequest.getDate(), reservationRequest.getTime(), experienceGift) + .orElseThrow(InvalidReservationException::new); + + if (reservation.getReservationStatus().equals(WAITING)) { + reservation.updateStatus(BOOKED); + reservation.updateUserReservationRequest(reservationRequest, sender, receiver); + naverSmsClient.sendApply(receiver, experienceGift, reservation); + experienceGift.addReservationCount(); + } else { + throw new InvalidReservationException(); + } + return ReservationResponse.toDtoUser(reservation); + } + + @Override + @Transactional + public ReservationResponse updateReservation(UpdateReservationReq updateReq, UserPrincipal userPrincipal) throws Exception { + User user = userRepository.findById(userPrincipal.getId()) + .orElseThrow(InvalidUserException::new); + //현재 예약 검색 + Reservation currentReservation = reservationRepository.findReservationById(updateReq.getReservationId()) + .orElseThrow(InvalidReservationException::new); + + if(!currentReservation.getSender().getId().equals(user.getId())) + throw new UserReservationMismatchException(); + + //변경 가능한 예약 검색 + Reservation newReservation = reservationRepository.findByDateAndTimeAndExperienceGift( + updateReq.getDate(), updateReq.getTime(), + currentReservation.getExperienceGift() + ).orElseThrow(InvalidReservationUpdateException::new); + + //DTO 생성 + UserReservationCreate reservationCreateDto = UserReservationCreate.builder() + .persons(currentReservation.getPersons()) + .experienceGiftId(currentReservation.getExperienceGift().getId()) + .invitationComment(currentReservation.getInvitationComment()) + .phoneNumber(currentReservation.getPhoneNumber()) + .imageUrl(currentReservation.getInvitationImg()) + .build(); + + //기존 예약 정보를 현재 예약 정보로 이동 + newReservation.updateUserReservationRequest(reservationCreateDto, + currentReservation.getSender(), currentReservation.getReceiver()); + newReservation.updateStatus(BOOKED); + Reservation updated = reservationRepository.save(newReservation); + + //기존 예약 정보 상태 초기화 + currentReservation.clearReservation(); + currentReservation.updateStatus(WAITING); + reservationRepository.save(currentReservation); + + naverSmsClient.sendChange(newReservation); + return ReservationResponse.toDtoUser(updated); } - return ReservationResponse.toDtoUser(reservation); - } - - @Transactional - public ReservationResponse updateReservation(UpdateReservationReq updateReq, - UserPrincipal userPrincipal) { - - //현재 예약 검색 - Reservation currentReservation = reservationRepository.findById(updateReq.getReservationId()) - .orElseThrow(InvalidReservationException::new); - - //변경 가능한 예약 검색 - Reservation newReservation = reservationRepository.findByDateAndTimeAndExperienceGift( - updateReq.getDate(), updateReq.getTime(), - currentReservation.getExperienceGift() - ).orElseThrow(InvalidReservationUpdateException::new); - - //DTO 생성 - UserReservationCreate reservationCreateDto = UserReservationCreate.builder() - .persons(currentReservation.getPersons()) - .experienceGiftId(currentReservation.getExperienceGift().getId()) - .invitationComment(currentReservation.getInvitationComment()) - .phoneNumber(currentReservation.getPhoneNumber()) - .imageUrl(currentReservation.getInvitationImg()) - .build(); - - //기존 예약 정보를 현재 예약 정보로 이동 - newReservation.updateUserReservationRequest(reservationCreateDto, - currentReservation.getSender(), currentReservation.getReceiver()); - newReservation.updateStatus(BOOKED); - Reservation updated = reservationRepository.save(newReservation); - - //기존 예약 정보 상태 초기화 - currentReservation.clearReservation(); - currentReservation.updateStatus(WAITING); - Reservation curr = reservationRepository.save(currentReservation); - - return ReservationResponse.toDtoUser(updated); - } @Override @Transactional @@ -136,11 +143,11 @@ public void cancelReservation(final UserPrincipal userPrincipal, final Long rese if (!reservation.getSender().getId().equals(user.getId())) throw new UserReservationMismatchException(); - if(reservation.getReservationStatus().equals(BOOKED)) { // BOOKED 상태일 때 WAITING으로 변경 + if (reservation.getReservationStatus().equals(BOOKED)) { // BOOKED 상태일 때 WAITING으로 변경 reservation.updateStatus(WAITING); naverSmsClient.sendCancel(reservation); reservation.clearReservation(); - } else if(reservation.getReservationStatus().equals(CONFIRMED)) { // CONFIRMED 상태일 때 CANCELLED로 변경 + } else if (reservation.getReservationStatus().equals(CONFIRMED)) { // CONFIRMED 상태일 때 CANCELLED로 변경 reservation.updateStatus(CANCELLED); naverSmsClient.sendCancel(reservation); } else { // 예약 상태가 BOOKED나 CONFIRMED가 아닐 경우 diff --git a/src/main/java/com/shallwe/domain/reservation/dto/request/UpdateReservationReq.java b/src/main/java/com/shallwe/domain/reservation/dto/request/UpdateReservationReq.java index dd9717f6..3dd25d2e 100644 --- a/src/main/java/com/shallwe/domain/reservation/dto/request/UpdateReservationReq.java +++ b/src/main/java/com/shallwe/domain/reservation/dto/request/UpdateReservationReq.java @@ -11,7 +11,7 @@ @Data public class UpdateReservationReq { - @Schema(type = "long", example = "2", description = "예약 날짜") + @Schema(type = "long", example = "2", description = "바꾸기 전 예약 ID") private Long reservationId; @Schema(type = "string", example = "2023-08-25", description = "예약 날짜") diff --git a/src/main/java/com/shallwe/domain/reservation/presentation/ReservationController.java b/src/main/java/com/shallwe/domain/reservation/presentation/ReservationController.java index 342e71de..7a572de8 100644 --- a/src/main/java/com/shallwe/domain/reservation/presentation/ReservationController.java +++ b/src/main/java/com/shallwe/domain/reservation/presentation/ReservationController.java @@ -143,7 +143,7 @@ public ResponseCustom createUserReservation( public ResponseCustom updateReservation( @Parameter(description = "수정 요청을 확인해주세요.", required = true) @RequestBody UpdateReservationReq updateReq, @Parameter(description = "AccessToken 을 입력해주세요.", required = true) @CurrentUser UserPrincipal userPrincipal - ) { + ) throws Exception { return ResponseCustom.OK( reservationManipulationService.updateReservation(updateReq, userPrincipal)); diff --git a/src/main/java/com/shallwe/global/infrastructure/sms/NaverSmsClient.java b/src/main/java/com/shallwe/global/infrastructure/sms/NaverSmsClient.java index ceb7834d..67d14cfc 100644 --- a/src/main/java/com/shallwe/global/infrastructure/sms/NaverSmsClient.java +++ b/src/main/java/com/shallwe/global/infrastructure/sms/NaverSmsClient.java @@ -296,6 +296,57 @@ public void sendCancel(Reservation reservation) throws Exception { .body(SmsResponseDto.class); } + public void sendChange(final Reservation reservation) throws Exception { + String timestamp = String.valueOf(System.currentTimeMillis()); + String url = "/alimtalk/v2/services/" + BIZTALK_SERVICE_ID + "/messages"; + String signature = makeSignature(timestamp, url); + + RestClient restClient = RestClient.builder() + .requestFactory(new HttpComponentsClientHttpRequestFactory()) + .baseUrl("https://sens.apigw.ntruss.com/alimtalk/v2") + .defaultHeaders(header -> { + header.set("Content-Type", "application/json"); + header.set("x-ncp-apigw-timestamp", timestamp); + header.set("x-ncp-iam-access-key", ACCESS_KEY); + header.set("x-ncp-apigw-signature-v2", signature); + }) + .build(); + + String date = reservation.getDate().toString(); + String time = reservation.getTime().toString(); + String receiveUserName = reservation.getReceiver().getName(); + String productName = reservation.getExperienceGift().getTitle(); + String persons = reservation.getPersons().toString() + "명"; + + List messages = new ArrayList<>(); + messages.add(MessageMapping.builder() + .to(reservation.getSender().getPhoneNumber()) + .content("[셸위]\n" + + "예약이 변경되었습니다\n" + + "\n" + + "예약날짜: " + date + "\n" + + "예약시간: " + time + "\n" + + "수취인: " + receiveUserName + "\n" + + "상품명: " + productName + "\n" + + "옵션: " + persons) + .build()); + + AlimTalkReq alimTalkReq = AlimTalkReq.builder() + .plusFriendId("@shallwee") + .templateCode("reservationChanges") + .messages(messages) + .build(); + + ObjectMapper objectMapper = new ObjectMapper(); + String body = objectMapper.writeValueAsString(alimTalkReq); + + restClient.post() + .uri("/services/" + BIZTALK_SERVICE_ID + "/messages") + .body(body) + .retrieve() + .body(SmsResponseDto.class); + } + @Transactional public Message validVerificationCode(ValidVerificationCodeReq validVerificationCodeReq) { LocalDateTime time = LocalDateTime.now();