diff --git a/src/main/java/site/goldenticket/domain/nego/service/NegoSchedulerService.java b/src/main/java/site/goldenticket/domain/nego/service/NegoSchedulerService.java index d473bbe8..39c09663 100644 --- a/src/main/java/site/goldenticket/domain/nego/service/NegoSchedulerService.java +++ b/src/main/java/site/goldenticket/domain/nego/service/NegoSchedulerService.java @@ -55,10 +55,10 @@ public void changeStatus() { //판매자에게 타임오버 알림 전송 alertService.createAlert(product.getUserId(), - "구매자가 20분 이내에 결제를 완료하지 않아 거래가 이루어지지 않았습니다."); + "구매자가 20분 이내에 결제를 완료하지 않아 거래가 종료되었습니다."); //구매자에게 타임오버 알림 전송 alertService.createAlert(nego.getUser().getId(), - "20분이 초과되었습니다. 아직 구매를 원하신다면, 재결제 버튼을 눌러 결제해주세요."); + "상품 결제 시간 20분이 초과되었습니다. 아직 구매를 원하신다면, 재결제 버튼을 눌러 결제를 완료해주세요."); //해당 상품 찜한 회원들에게 알림 전송 if (product.getProductStatus().equals(ProductStatus.SELLING)) { alertService.createAlertOfWishProductToSelling(product.getId(), @@ -103,16 +103,9 @@ public void changeStatus() { // 판매자에게 정산 요청 알림 전송 alertService.createAlert(product.getUserId(), "'" + product.getAccommodationName() + "(" + product.getRoomName() - + ")'상품 양도가 완료되었습니다. 영업일 1일 이내 등록한 계좌 정보로 정산 금액이 입금됩니다." + + ")'상품 양도가 완료되었습니다. 최대 9영업일 이내에 등록한 계좌 정보로 정산 금액이 입금됩니다." + "원활한 정산 진행을 위해 '마이페이지 - 나의 계좌'정보를 다시 한번 확인해주세요."); - // 판매자에게 계좌 등록 알림 전송 - if (user != null && user.getAccountNumber() == null) { - alertService.createAlert(product.getUserId(), - "'" + product.getAccommodationName() + "(" + product.getRoomName() - + ")'상품에 대한 원활한 정산을 위해 '마이페이지 > 내 계좌'에서 입금받으실 계좌를 등록해주세요."); - } - //자동 양도완료 관련 시스템 메세지 전송 chatService.createSystemMessageOfCompletedTransferByScheduler(product.getId(), product.getUserId(), transferNego.getUser().getId()); diff --git a/src/main/java/site/goldenticket/domain/nego/service/NegoServiceImpl.java b/src/main/java/site/goldenticket/domain/nego/service/NegoServiceImpl.java index d5079235..b06e8893 100644 --- a/src/main/java/site/goldenticket/domain/nego/service/NegoServiceImpl.java +++ b/src/main/java/site/goldenticket/domain/nego/service/NegoServiceImpl.java @@ -1,5 +1,8 @@ package site.goldenticket.domain.nego.service; +import java.time.ZoneId; +import java.time.ZonedDateTime; +import java.time.format.DateTimeFormatter; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Service; @@ -67,11 +70,17 @@ public NegoResponse confirmPrice(Long negoId, PrincipalDetails principalDetails) productService.updateProductForNego(product); negoRepository.save(nego); - //구매자에게 네고 승인 및 결제 안내 알림 전송 + //구매자에게 네고 승인 및 20분 내 결제 안내 알림 전송 + ZoneId koreaZoneId = ZoneId.of("Asia/Seoul"); + ZonedDateTime koreaZonedDateTimeOfExpirationTime = nego.getExpirationTime().atZone(koreaZoneId); + DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy.MM.dd HH:mm:ss"); + String formattedDateTimeOfExpirationTime = koreaZonedDateTimeOfExpirationTime.format(formatter); + String formattedDateTimeWithoutSeconds =formattedDateTimeOfExpirationTime + .substring(0, formattedDateTimeOfExpirationTime.length() - 3); alertService.createAlert(nego.getUser().getId(), "'" + nego.getProduct().getAccommodationName() + "(" + nego.getProduct().getRoomName() + ")'상품에 대한 네고 요청이 승인되었습니다. 해당상품을 " - + nego.getExpirationTime() + "까지 결제를 완료해주세요."); + + formattedDateTimeWithoutSeconds+ "까지 결제를 완료해주세요."); } else { // 다른 상태의 네고는 가격 승낙을 처리할 수 없음 throw new CustomException("네고를 승인할수 없습니다.", ErrorCode.CANNOT_CONFIRM_NEGO); @@ -191,7 +200,7 @@ public PriceProposeResponse proposePrice(Long productId, PriceProposeRequest req //판매자에게 네고 제안 알림 전송 alertService.createAlert(product.getUserId(), - "'" + product.getAccommodationName() + "(" + product.getRoomName() + "판매중인 상품 '" + product.getAccommodationName() + "(" + product.getRoomName() + ")'상품에 대한 네고 요청이 들어왔습니다."); return PriceProposeResponse.fromEntity(userNego); @@ -399,15 +408,8 @@ private void sendTransferCompleteAlerts(Nego nego, Product product, User user) { // 판매자에게 정산 요청 알림 전송 alertService.createAlert(product.getUserId(), "'" + product.getAccommodationName() + "(" + product.getRoomName() - + ")'상품 양도가 완료되었습니다. 영업일 1일 이내 등록한 계좌 정보로 정산 금액이 입금됩니다." + + ")'상품 양도가 완료되었습니다. 최대 9영업일 이내에 등록한 계좌 정보로 정산 금액이 입금됩니다." + "원활한 정산 진행을 위해 '마이페이지 - 나의 계좌'정보를 다시 한번 확인해주세요."); - - // 판매자에게 계좌 등록 알림 전송 - if (user != null && user.getAccountNumber() == null) { - alertService.createAlert(product.getUserId(), - "'" + product.getAccommodationName() + "(" + product.getRoomName() - + ")'상품에 대한 원활한 정산을 위해 '마이페이지 > 내 계좌'에서 입금받으실 계좌를 등록해주세요."); - } } private void sendTransferCompleteAlertsForNotNego(Order order, Product product, User user) { @@ -422,15 +424,8 @@ private void sendTransferCompleteAlertsForNotNego(Order order, Product product, // 판매자에게 정산 요청 알림 전송 alertService.createAlert(product.getUserId(), "'" + product.getAccommodationName() + "(" + product.getRoomName() - + ")'상품 양도가 완료되었습니다. 영업일 1일 이내 등록한 계좌 정보로 정산 금액이 입금됩니다." + + ")'상품 양도가 완료되었습니다. 최대 9영업일 등록한 계좌 정보로 정산 금액이 입금됩니다." + "원활한 정산 진행을 위해 '마이페이지 - 나의 계좌'정보를 다시 한번 확인해주세요."); - - // 판매자에게 계좌 등록 알림 전송 - if (user.getAccountNumber() == null) { - alertService.createAlert(product.getUserId(), - "'" + product.getAccommodationName() + "(" + product.getRoomName() - + ")'상품에 대한 원활한 정산을 위해 '마이페이지 > 내 계좌'에서 입금받으실 계좌를 등록해주세요."); - } } } diff --git a/src/main/java/site/goldenticket/domain/payment/service/PaymentService.java b/src/main/java/site/goldenticket/domain/payment/service/PaymentService.java index d6f0a347..ca331d3b 100644 --- a/src/main/java/site/goldenticket/domain/payment/service/PaymentService.java +++ b/src/main/java/site/goldenticket/domain/payment/service/PaymentService.java @@ -1,5 +1,8 @@ package site.goldenticket.domain.payment.service; +import java.time.ZoneId; +import java.time.ZonedDateTime; +import java.time.format.DateTimeFormatter; import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; @@ -49,7 +52,8 @@ public class PaymentService { private final AlertService alertService; private final ChatService chatService; - public PaymentDetailResponse getPaymentDetail(Long productId, PrincipalDetails principalDetails) { + public PaymentDetailResponse getPaymentDetail(Long productId, + PrincipalDetails principalDetails) { User user = userService.findById(principalDetails.getUserId()); Product product = productService.getProduct(productId); @@ -58,7 +62,8 @@ public PaymentDetailResponse getPaymentDetail(Long productId, PrincipalDetails p } //상품상태 = 예약중, 해당 경우에 본인이 네고를 진행하였고 결제 대기중인지 확인 - Optional nego = negoRepository.findFirstByUser_IdAndProduct_IdOrderByCreatedAtDesc(user.getId(), product.getId()); + Optional nego = negoRepository.findFirstByUser_IdAndProduct_IdOrderByCreatedAtDesc( + user.getId(), product.getId()); if (product.getProductStatus() == ProductStatus.RESERVED) { if (nego.isEmpty()) { throw new CustomException(ErrorCode.PRODUCT_NOT_ON_SALE); @@ -85,7 +90,8 @@ public PaymentDetailResponse getPaymentDetail(Long productId, PrincipalDetails p Order savedOrder = orderRepository.save(order); - return PaymentDetailResponse.of(savedOrder.getId(), user, product, price, product.getYanoljaPrice()); + return PaymentDetailResponse.of(savedOrder.getId(), user, product, price, + product.getYanoljaPrice()); } public PaymentReadyResponse preparePayment(Long orderId, PrincipalDetails principalDetails) { @@ -93,7 +99,7 @@ public PaymentReadyResponse preparePayment(Long orderId, PrincipalDetails princi User user = userService.findById(principalDetails.getUserId()); Order order = orderRepository.findById(orderId).orElseThrow( - () -> new CustomException(ErrorCode.ORDER_NOT_FOUND) + () -> new CustomException(ErrorCode.ORDER_NOT_FOUND) ); Product product = productService.getProduct(order.getProductId()); @@ -102,7 +108,8 @@ public PaymentReadyResponse preparePayment(Long orderId, PrincipalDetails princi } //상품상태 = 예약중, 해당 경우에 본인이 네고를 진행하였고 결제 대기중인지 확인 - Optional nego = negoRepository.findFirstByUser_IdAndProduct_IdOrderByCreatedAtDesc(user.getId(), product.getId()); + Optional nego = negoRepository.findFirstByUser_IdAndProduct_IdOrderByCreatedAtDesc( + user.getId(), product.getId()); if (product.getProductStatus() == ProductStatus.RESERVED) { if (nego.isEmpty()) { throw new CustomException(ErrorCode.PRODUCT_NOT_ON_SALE); @@ -123,10 +130,10 @@ public PaymentResponse savePayment(PaymentRequest request, PrincipalDetails prin Long userId = principalDetails.getUserId(); Payment payment = iamportRepository.findPaymentByImpUid(request.getImpUid()) - .orElseThrow(() -> new CustomException(ErrorCode.PAYMENT_NOT_FOUND)); + .orElseThrow(() -> new CustomException(ErrorCode.PAYMENT_NOT_FOUND)); Order order = orderRepository.findById(request.getOrderId()) - .orElseThrow(() -> new CustomException(ErrorCode.ORDER_NOT_FOUND)); + .orElseThrow(() -> new CustomException(ErrorCode.ORDER_NOT_FOUND)); Product product = productService.getProduct(order.getProductId()); @@ -148,7 +155,8 @@ public PaymentResponse savePayment(PaymentRequest request, PrincipalDetails prin return PaymentResponse.failed(); } - Optional nego = negoRepository.findFirstByUser_IdAndProduct_IdOrderByCreatedAtDesc(userId, product.getId()); + Optional nego = negoRepository.findFirstByUser_IdAndProduct_IdOrderByCreatedAtDesc( + userId, product.getId()); //상품상태 = 예약중 if (product.getProductStatus() == ProductStatus.RESERVED) { @@ -180,18 +188,29 @@ public PaymentResponse savePayment(PaymentRequest request, PrincipalDetails prin order.waitTransfer(); product.setProductStatus(ProductStatus.RESERVED); - //판매자에게 양도 알림 전송 + //판매자에게 20분 내 양도 알림 전송 + ZoneId koreaZoneId = ZoneId.of("Asia/Seoul"); + ZonedDateTime koreaZonedDateTimeOfExpirationTime = order.getUpdatedAt().plusMinutes(20) + .atZone(koreaZoneId); + Integer hour = koreaZonedDateTimeOfExpirationTime.getHour(); + Integer minute = koreaZonedDateTimeOfExpirationTime.getMinute(); + DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy.MM.dd HH:mm:ss"); + String formattedDateTimeOfExpirationTime = koreaZonedDateTimeOfExpirationTime.format( + formatter); + String formattedDateTimeWithoutSeconds =formattedDateTimeOfExpirationTime + .substring(0, formattedDateTimeOfExpirationTime.length() - 3); alertService.createAlert(product.getUserId(), - product.getAccommodationName() + "(" + product.getRoomName() + ") " - + "상품이 결제완료되었습니다." + order.getUpdatedAt().plusHours(3) - + "까지 양도 신청을 완료해주세요. 양도 미신청 시, 자동 양도 신청됩니다."); + product.getAccommodationName() + "(" + product.getRoomName() + ") " + + "상품이 결제완료되었습니다." + formattedDateTimeWithoutSeconds + + "'까지 양도 여부를 선택해주세요. 미 입력 시 "+hour+":"+minute+" 이후 자동으로 양도가 진행됩니다."); //채팅방 생성 + 시작 메세지 생성 if (!chatService.existsChatRoomByBuyerIdAndProductId(userId, product.getId())) { ChatRoomResponse chatRoomResponse = chatService.createChatRoom(userId, product.getId()); chatService.createStartMessageOfNewChatRoom(chatRoomResponse.chatRoomId()); } - Long chatRoomId = chatService.getChatRoomByBuyerIdAndProductId(userId, product.getId()).getId(); + Long chatRoomId = chatService.getChatRoomByBuyerIdAndProductId(userId, product.getId()) + .getId(); return PaymentResponse.success(chatRoomId); } @@ -201,19 +220,20 @@ public Optional findByProductIdAndStatus(Long productId, OrderStatus orde } public void cancelPayment(String impUid) { - List paymentCancelDetails = iamportRepository.cancelPaymentByImpUid(impUid); + List paymentCancelDetails = iamportRepository.cancelPaymentByImpUid( + impUid); paymentCancelDetailRepository.saveAll(paymentCancelDetails); String pgTid = paymentCancelDetails.get(0).getPgTid(); Payment payment = paymentRepository.findByPgTid(pgTid).orElseThrow( - () -> new CustomException(ErrorCode.PAYMENT_NOT_FOUND) + () -> new CustomException(ErrorCode.PAYMENT_NOT_FOUND) ); payment.cancelledPayment(); } public Payment findByOrderId(Long orderId) { return paymentRepository.findByOrderId(orderId).orElseThrow( - () -> new CustomException(ErrorCode.PAYMENT_NOT_FOUND) + () -> new CustomException(ErrorCode.PAYMENT_NOT_FOUND) ); } }