From 0f8006c86242c8e44c08e03573b23f745ecc3f38 Mon Sep 17 00:00:00 2001 From: nowgnas Date: Wed, 10 Jan 2024 00:08:16 +0900 Subject: [PATCH] feat: event duplication processing --- build.gradle | 2 +- .../DuplicateEventHandleAnnotation.java | 6 +- .../common/aop/DuplicateEventHandlerAop.java | 60 ++-- .../emitter/application/SseService.java | 1 + .../entity/NotificationCommand.java | 6 + .../helper/NotificationActionHelper.java | 274 +++++++++--------- .../message/NotificationSQSListener.java | 6 +- .../infrastructure/sms/SendSMS.java | 2 + .../infrastructure/sse/SendSSE.java | 2 + 9 files changed, 184 insertions(+), 175 deletions(-) diff --git a/build.gradle b/build.gradle index ff78305..a2fae66 100644 --- a/build.gradle +++ b/build.gradle @@ -44,7 +44,7 @@ dependencies { implementation 'org.springframework.cloud:spring-cloud-aws-messaging:2.2.4.RELEASE' implementation 'org.springframework.kafka:spring-kafka' implementation 'software.amazon.awssdk:sns:2.21.37' - implementation 'io.github.lotteon-maven:blooming-blooms-utils:202401081357' + implementation 'io.github.lotteon-maven:blooming-blooms-utils:202401091420' // https://mvnrepository.com/artifact/org.springframework.boot/spring-boot-starter-aop implementation 'org.springframework.boot:spring-boot-starter-aop:2.7.17' diff --git a/src/main/java/kr/bb/notification/common/annotation/DuplicateEventHandleAnnotation.java b/src/main/java/kr/bb/notification/common/annotation/DuplicateEventHandleAnnotation.java index be516fa..1783f4c 100644 --- a/src/main/java/kr/bb/notification/common/annotation/DuplicateEventHandleAnnotation.java +++ b/src/main/java/kr/bb/notification/common/annotation/DuplicateEventHandleAnnotation.java @@ -8,9 +8,7 @@ @Target(ElementType.METHOD) @Retention(RetentionPolicy.RUNTIME) public @interface DuplicateEventHandleAnnotation { - String eventId(); // Specify eventId for constructing the cache key + String getEventType(); - String userId(); // Specify userId for constructing the cache key - - long ttl() default 180; // Default TTL is 3 minutes + long getTtl() default 1L; } diff --git a/src/main/java/kr/bb/notification/common/aop/DuplicateEventHandlerAop.java b/src/main/java/kr/bb/notification/common/aop/DuplicateEventHandlerAop.java index 2b1b15b..af19e9a 100644 --- a/src/main/java/kr/bb/notification/common/aop/DuplicateEventHandlerAop.java +++ b/src/main/java/kr/bb/notification/common/aop/DuplicateEventHandlerAop.java @@ -1,8 +1,11 @@ package kr.bb.notification.common.aop; +import bloomingblooms.domain.notification.Role; +import java.util.concurrent.TimeUnit; +import kr.bb.notification.common.annotation.DuplicateEventHandleAnnotation; +import kr.bb.notification.domain.notification.entity.NotificationCommand.NotificationInformation; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; -import org.aspectj.lang.JoinPoint; import org.aspectj.lang.ProceedingJoinPoint; import org.aspectj.lang.annotation.Around; import org.aspectj.lang.annotation.Aspect; @@ -16,33 +19,36 @@ @RequiredArgsConstructor public class DuplicateEventHandlerAop { private final RedisTemplate redisTemplate; - // https://alwayspr.tistory.com/34 - - @Pointcut("@annotation(kr.bb.notification.common.annotation.DuplicateEventHandleAnnotation)") - public void duplicateEvent() {} - - @Around("duplicateEvent()") - public Object duplicateEventHandlerAop(ProceedingJoinPoint joinPoint) throws Throwable { - String eventId = null; - Object[] args = joinPoint.getArgs(); - - // Check if data exists in Redis - Object cachedData = redisTemplate.opsForValue().get(eventId); - - Object result = joinPoint.proceed(); // run method - - if (cachedData != null) { - // Data exists in cache, return it without executing the method - return cachedData; + @Pointcut("@annotation(duplicateEventHandleAnnotation)") + public void duplicateEvent(DuplicateEventHandleAnnotation duplicateEventHandleAnnotation) {} + + @Around( + value = "duplicateEvent(duplicateEventHandleAnnotation) && args(notifyData)", + argNames = "joinPoint,notifyData,duplicateEventHandleAnnotation") + public Object duplicateEventHandlerAop( + ProceedingJoinPoint joinPoint, + NotificationInformation notifyData, + DuplicateEventHandleAnnotation duplicateEventHandleAnnotation) + throws Throwable { + String eventId = notifyData.getEventId(); + String id = String.valueOf(notifyData.getId()); + Role role = notifyData.getRole(); + String eventType = duplicateEventHandleAnnotation.getEventType(); + long ttl = duplicateEventHandleAnnotation.getTtl(); + + String generateEventId = eventId + ":" + id + ":" + role + ":" + eventType; + Object event = redisTemplate.opsForValue().get(generateEventId); + log.info("event id : " + generateEventId); + if (event == null) { + redisTemplate.opsForValue().set(generateEventId, eventType); + redisTemplate.expire(generateEventId, ttl, TimeUnit.MINUTES); // TODO: test 후 Days로 변경 + + Object[] args = joinPoint.getArgs(); + + return joinPoint.proceed(args); + } else { + return null; } - - // Data doesn't exist in cache, proceed to execute the method - Object result = joinPoint.proceed(); - - // Save the result in Redis with the specified TTL - redisTemplate.opsForValue().set(key, result, duplicateEventHandlerAop.ttl()); - - return result; } } diff --git a/src/main/java/kr/bb/notification/domain/emitter/application/SseService.java b/src/main/java/kr/bb/notification/domain/emitter/application/SseService.java index 065c1c1..51d6641 100644 --- a/src/main/java/kr/bb/notification/domain/emitter/application/SseService.java +++ b/src/main/java/kr/bb/notification/domain/emitter/application/SseService.java @@ -1,6 +1,7 @@ package kr.bb.notification.domain.emitter.application; import java.io.IOException; +import kr.bb.notification.common.annotation.DuplicateEventHandleAnnotation; import kr.bb.notification.domain.emitter.repository.SSERepository; import kr.bb.notification.domain.notification.entity.NotificationCommand.NotificationInformation; import lombok.RequiredArgsConstructor; diff --git a/src/main/java/kr/bb/notification/domain/notification/entity/NotificationCommand.java b/src/main/java/kr/bb/notification/domain/notification/entity/NotificationCommand.java index e7b3c27..7cdb6e2 100644 --- a/src/main/java/kr/bb/notification/domain/notification/entity/NotificationCommand.java +++ b/src/main/java/kr/bb/notification/domain/notification/entity/NotificationCommand.java @@ -85,6 +85,7 @@ public static class NotificationInformation { private String phoneNumber; private Role role; private NotificationKind notificationKind; + private String eventId; public static List getResaleNotificationData( NotificationData restoreNotification) { @@ -104,6 +105,7 @@ public static List getResaleNotificationData( .notificationKind( restoreNotification.getPublishInformation().getNotificationKind()) .redirectUrl(restoreNotification.getPublishInformation().getNotificationUrl()) + .eventId(restoreNotification.getPublishInformation().getEventId()) .build()) .collect(Collectors.toList()); } @@ -112,6 +114,7 @@ public static NotificationInformation getSSEData( PublishNotificationInformation publishNotificationInformation, Long id) { return NotificationInformation.builder() .id(id) + .eventId(publishNotificationInformation.getEventId()) .role(publishNotificationInformation.getRole()) .notificationKind(publishNotificationInformation.getNotificationKind()) .redirectUrl(publishNotificationInformation.getNotificationUrl()) @@ -122,6 +125,7 @@ public static NotificationInformation getSSEData( public static NotificationInformation getDeliveryNotificationData( NotificationData notificationData) { return NotificationInformation.builder() + .eventId(notificationData.getPublishInformation().getEventId()) .role(notificationData.getPublishInformation().getRole()) .phoneNumber(notificationData.getWhoToNotify().getPhoneNumber()) .type(notificationData.getWhoToNotify().getDeliveryStatus().getMessage()) @@ -134,6 +138,7 @@ public static NotificationInformation getDeliveryNotificationData( public static NotificationInformation getSMSData( NotificationData notification) { return NotificationInformation.builder() + .eventId(notification.getPublishInformation().getEventId()) .notificationKind(notification.getPublishInformation().getNotificationKind()) .role(notification.getPublishInformation().getRole()) .id(notification.getWhoToNotify().getUserId()) @@ -146,6 +151,7 @@ public static NotificationInformation getSMSData( public static NotificationInformation getNewOrderStatusData( NotificationData notification) { return NotificationInformation.builder() + .eventId(notification.getPublishInformation().getEventId()) .notificationKind(notification.getPublishInformation().getNotificationKind()) .phoneNumber(notification.getWhoToNotify().getPhoneNumber()) .redirectUrl(notification.getPublishInformation().getNotificationUrl()) diff --git a/src/main/java/kr/bb/notification/domain/notification/helper/NotificationActionHelper.java b/src/main/java/kr/bb/notification/domain/notification/helper/NotificationActionHelper.java index 3bb8620..a626c52 100644 --- a/src/main/java/kr/bb/notification/domain/notification/helper/NotificationActionHelper.java +++ b/src/main/java/kr/bb/notification/domain/notification/helper/NotificationActionHelper.java @@ -24,145 +24,137 @@ @RequiredArgsConstructor public class NotificationActionHelper { - private final SendSMS sms; - private final SendSSE sse; - private final NotificationCommandService notificationCommandService; - - public void publishResaleNotification(NotificationData notification) { - List data = - NotificationInformation.getResaleNotificationData(notification); - data.forEach( - item -> { - sms.publish(item); - sse.publish(item); - }); - - // save notification - notificationCommandService.saveResaleNotification(notification); - } - - public void publishQuestionRegisterNotification( - NotificationData notification) { - NotificationInformation sseNotification = - NotificationInformation.getSSEData( - notification.getPublishInformation(), - notification.getWhoToNotify().getStoreId()); - sse.publish(sseNotification); - - // save notification - notificationCommandService.saveSingleNotification( - notification.getPublishInformation(), notification.getWhoToNotify().getStoreId()); - } - - public void publishNewComerNotification(NotificationData notification) { - NotificationInformation sseNotification = - NotificationInformation.getSSEData(notification.getPublishInformation(), 100L); - sse.publish(sseNotification); - - // save notification - notificationCommandService.saveSingleNotification(notification.getPublishInformation(), 100L); - } - - public void publishNewOrderNotification(NotificationData notification) { - NotificationInformation sseNotification = - NotificationInformation.getSSEData( - notification.getPublishInformation(), - notification.getWhoToNotify().getStoreId()); - sse.publish(sseNotification); - - // save notification - notificationCommandService.saveSingleNotification( - notification.getPublishInformation(), notification.getWhoToNotify().getStoreId()); - } - - public void publishOutOfStockNotification( - NotificationData outOfStockNotification) { - NotificationInformation sseData = - NotificationInformation.getSSEData( - outOfStockNotification.getPublishInformation(), - outOfStockNotification.getWhoToNotify().getStoreId()); - sse.publish(sseData); - - notificationCommandService.saveSingleNotification( - outOfStockNotification.getPublishInformation(), - outOfStockNotification.getWhoToNotify().getStoreId()); - } - - public void publishDeliveryStartNotification( - NotificationData notificationData) { - NotificationInformation notifyData = - NotificationInformation.getDeliveryNotificationData(notificationData); - sse.publish(notifyData); - sms.publish(notifyData); - - // save notification - notificationCommandService.saveSingleNotification( - notificationData.getPublishInformation(), - notificationData.getWhoToNotify().getUserId()); - } - - public void publishSettlementNotification( - NotificationData notification) { - NotificationInformation sseData = - NotificationInformation.getSSEData( - notification.getPublishInformation(), - notification.getWhoToNotify().getStoreId()); - sse.publish(sseData); - - notificationCommandService.saveSingleNotification( - notification.getPublishInformation(), notification.getWhoToNotify().getStoreId()); - } - - public void publishOrderCancelNotification( - NotificationData notification) { - NotificationInformation sseData = - NotificationInformation.getSSEData( - notification.getPublishInformation(), - notification.getWhoToNotify().getStoreId()); - sse.publish(sseData); - - notificationCommandService.saveSingleNotification( - notification.getPublishInformation(), notification.getWhoToNotify().getStoreId()); - } - - /** - * 문의 답변 등록 알림 - * - * @param notification - */ - public void publishInqueryResponseNotification( - NotificationData notification) { - NotificationInformation sseData = - NotificationInformation.getSSEData( - notification.getPublishInformation(), - notification.getWhoToNotify().getUserId()); - NotificationInformation smsData = NotificationInformation.getSMSData(notification); - sse.publish(sseData); - sms.publish(smsData); - - // save notification - notificationCommandService.saveSingleNotification( - notification.getPublishInformation(), notification.getWhoToNotify().getUserId()); - } - - /** - * 주문 상태 알림 - * - * @param notification - */ - public void publishNewOrderStatusNotification( - NotificationData notification) { - NotificationInformation sseData = - NotificationInformation.getSSEData( - notification.getPublishInformation(), - notification.getWhoToNotify().getUserId()); - NotificationInformation smsData = NotificationInformation.getNewOrderStatusData( - notification); - sse.publish(sseData); - sms.publish(smsData); - - // save notification - notificationCommandService.saveSingleNotification( - notification.getPublishInformation(), notification.getWhoToNotify().getUserId()); - } + private final SendSMS sms; + private final SendSSE sse; + private final NotificationCommandService notificationCommandService; + + public void publishResaleNotification(NotificationData notification) { + List data = + NotificationInformation.getResaleNotificationData(notification); + data.forEach( + item -> { + sms.publish(item); + sse.publish(item); + }); + + // save notification + notificationCommandService.saveResaleNotification(notification); + } + + public void publishQuestionRegisterNotification(NotificationData notification) { + NotificationInformation sseNotification = + NotificationInformation.getSSEData( + notification.getPublishInformation(), notification.getWhoToNotify().getStoreId()); + sse.publish(sseNotification); + + // save notification + notificationCommandService.saveSingleNotification( + notification.getPublishInformation(), notification.getWhoToNotify().getStoreId()); + } + + public void publishNewComerNotification(NotificationData notification) { + NotificationInformation sseNotification = + NotificationInformation.getSSEData(notification.getPublishInformation(), 100L); + sse.publish(sseNotification); + + // save notification + notificationCommandService.saveSingleNotification(notification.getPublishInformation(), 100L); + } + + public void publishNewOrderNotification(NotificationData notification) { + NotificationInformation sseNotification = + NotificationInformation.getSSEData( + notification.getPublishInformation(), notification.getWhoToNotify().getStoreId()); + sse.publish(sseNotification); + + // save notification + notificationCommandService.saveSingleNotification( + notification.getPublishInformation(), notification.getWhoToNotify().getStoreId()); + } + + public void publishOutOfStockNotification( + NotificationData outOfStockNotification) { + NotificationInformation sseData = + NotificationInformation.getSSEData( + outOfStockNotification.getPublishInformation(), + outOfStockNotification.getWhoToNotify().getStoreId()); + log.info(String.valueOf(sseData.getId())); + log.info(String.valueOf(sseData.getRole())); + sse.publish(sseData); + + notificationCommandService.saveSingleNotification( + outOfStockNotification.getPublishInformation(), + outOfStockNotification.getWhoToNotify().getStoreId()); + } + + public void publishDeliveryStartNotification( + NotificationData notificationData) { + NotificationInformation notifyData = + NotificationInformation.getDeliveryNotificationData(notificationData); + sse.publish(notifyData); + sms.publish(notifyData); + + // save notification + notificationCommandService.saveSingleNotification( + notificationData.getPublishInformation(), notificationData.getWhoToNotify().getUserId()); + } + + public void publishSettlementNotification(NotificationData notification) { + NotificationInformation sseData = + NotificationInformation.getSSEData( + notification.getPublishInformation(), notification.getWhoToNotify().getStoreId()); + sse.publish(sseData); + + notificationCommandService.saveSingleNotification( + notification.getPublishInformation(), notification.getWhoToNotify().getStoreId()); + } + + public void publishOrderCancelNotification( + NotificationData notification) { + NotificationInformation sseData = + NotificationInformation.getSSEData( + notification.getPublishInformation(), notification.getWhoToNotify().getStoreId()); + sse.publish(sseData); + + notificationCommandService.saveSingleNotification( + notification.getPublishInformation(), notification.getWhoToNotify().getStoreId()); + } + + /** + * 문의 답변 등록 알림 + * + * @param notification + */ + public void publishInqueryResponseNotification( + NotificationData notification) { + NotificationInformation sseData = + NotificationInformation.getSSEData( + notification.getPublishInformation(), notification.getWhoToNotify().getUserId()); + NotificationInformation smsData = NotificationInformation.getSMSData(notification); + sse.publish(sseData); + sms.publish(smsData); + + // save notification + notificationCommandService.saveSingleNotification( + notification.getPublishInformation(), notification.getWhoToNotify().getUserId()); + } + + /** + * 주문 상태 알림 + * + * @param notification + */ + public void publishNewOrderStatusNotification( + NotificationData notification) { + NotificationInformation sseData = + NotificationInformation.getSSEData( + notification.getPublishInformation(), notification.getWhoToNotify().getUserId()); + NotificationInformation smsData = NotificationInformation.getNewOrderStatusData(notification); + sse.publish(sseData); + sms.publish(smsData); + + // save notification + notificationCommandService.saveSingleNotification( + notification.getPublishInformation(), notification.getWhoToNotify().getUserId()); + } } diff --git a/src/main/java/kr/bb/notification/domain/notification/infrastructure/message/NotificationSQSListener.java b/src/main/java/kr/bb/notification/domain/notification/infrastructure/message/NotificationSQSListener.java index 1abdcc3..bb9cfcb 100644 --- a/src/main/java/kr/bb/notification/domain/notification/infrastructure/message/NotificationSQSListener.java +++ b/src/main/java/kr/bb/notification/domain/notification/infrastructure/message/NotificationSQSListener.java @@ -125,7 +125,8 @@ public void consumeNewOrderNotificationQueue( PublishNotificationInformation.makePublishNotificationInformation( NewOrderEventItem.getNotificationURL(item.getOrderType()), NewOrderEventItem.getNotificationKind(item.getOrderType()), - Role.MANAGER)); + Role.MANAGER, + "new-order-queue")); notificationActionHelper.publishNewOrderNotification(notificationData); }); @@ -307,7 +308,8 @@ public void consumeInqueryResponseNotificationQueue( notificationActionHelper.publishInqueryResponseNotification(notification); ack.acknowledge(); } - @SqsListener( + + @SqsListener( value = "${cloud.aws.sqs.new-order-status-queue.name}", deletionPolicy = SqsMessageDeletionPolicy.NEVER) public void consumeNewOrderStatusQueue( diff --git a/src/main/java/kr/bb/notification/domain/notification/infrastructure/sms/SendSMS.java b/src/main/java/kr/bb/notification/domain/notification/infrastructure/sms/SendSMS.java index bca0c5b..e4b5b4a 100644 --- a/src/main/java/kr/bb/notification/domain/notification/infrastructure/sms/SendSMS.java +++ b/src/main/java/kr/bb/notification/domain/notification/infrastructure/sms/SendSMS.java @@ -2,6 +2,7 @@ import java.util.HashMap; import java.util.Map; +import kr.bb.notification.common.annotation.DuplicateEventHandleAnnotation; import kr.bb.notification.config.AWSConfiguration; import kr.bb.notification.domain.notification.entity.NotificationCommand.NotificationInformation; import kr.bb.notification.domain.notification.infrastructure.action.InfrastructureActionHandler; @@ -46,6 +47,7 @@ private static void setSMSAttribute(SnsClient snsClient) { snsClient.setSMSAttributes(request); } + @DuplicateEventHandleAnnotation(getEventType = "sms") @Override public void publish(NotificationInformation notifyData) { SnsClient snsClient = awsConfiguration.snsClient(); diff --git a/src/main/java/kr/bb/notification/domain/notification/infrastructure/sse/SendSSE.java b/src/main/java/kr/bb/notification/domain/notification/infrastructure/sse/SendSSE.java index 41748b5..3e26052 100644 --- a/src/main/java/kr/bb/notification/domain/notification/infrastructure/sse/SendSSE.java +++ b/src/main/java/kr/bb/notification/domain/notification/infrastructure/sse/SendSSE.java @@ -1,5 +1,6 @@ package kr.bb.notification.domain.notification.infrastructure.sse; +import kr.bb.notification.common.annotation.DuplicateEventHandleAnnotation; import kr.bb.notification.domain.emitter.application.SseService; import kr.bb.notification.domain.notification.entity.NotificationCommand.NotificationInformation; import kr.bb.notification.domain.notification.infrastructure.action.InfrastructureActionHandler; @@ -13,6 +14,7 @@ public class SendSSE implements InfrastructureActionHandler { private final SseService sseService; + @DuplicateEventHandleAnnotation(getEventType = "sse") @Override public void publish(NotificationInformation notifyData) { sseService.notify(notifyData);