diff --git a/build.gradle b/build.gradle index 31eff8c1..310c91f3 100644 --- a/build.gradle +++ b/build.gradle @@ -63,6 +63,8 @@ dependencies { //AWS implementation 'org.springframework.cloud:spring-cloud-starter-aws:2.2.6.RELEASE' + implementation platform("io.awspring.cloud:spring-cloud-aws-dependencies:3.0.0") + implementation 'io.awspring.cloud:spring-cloud-aws-starter-sns' //swagger implementation 'org.springdoc:springdoc-openapi-starter-webmvc-ui:2.1.0' diff --git a/src/main/java/com/gloddy/server/apply/application/ApplyService.java b/src/main/java/com/gloddy/server/apply/application/ApplyService.java index 49f040cc..e15d7bea 100644 --- a/src/main/java/com/gloddy/server/apply/application/ApplyService.java +++ b/src/main/java/com/gloddy/server/apply/application/ApplyService.java @@ -7,6 +7,7 @@ import com.gloddy.server.apply.domain.handler.ApplyQueryHandler; import com.gloddy.server.apply.domain.service.*; import com.gloddy.server.apply.domain.vo.Status; +import com.gloddy.server.apply.event.ApplyCreateEvent; import com.gloddy.server.apply.event.producer.ApplyEventProducer; import com.gloddy.server.auth.domain.User; import com.gloddy.server.core.event.GroupParticipateEvent; @@ -30,6 +31,7 @@ public class ApplyService { private final GroupQueryHandler groupQueryHandler; private final RejectedApplyCheckExecutor rejectedApplyCheckExecutor; private final ApplyStatusUpdateExecutor applyStatusUpdateExecutor; + private final ApplyEventProducer applyEventProducer; @Transactional public ApplyResponse.Create createApply(Long userId, Long groupId, ApplyRequest.Create request) { @@ -38,6 +40,7 @@ public ApplyResponse.Create createApply(Long userId, Long groupId, ApplyRequest. Apply apply = applyCommandHandler.save( group.createApply(user, request.getIntroduce(), request.getReason()) ); + applyEventProducer.produceEvent(new ApplyCreateEvent(userId, groupId, apply.getId())); return new ApplyResponse.Create(apply.getId()); } diff --git a/src/main/java/com/gloddy/server/apply/domain/service/ApplyStatusUpdateExecutor.java b/src/main/java/com/gloddy/server/apply/domain/service/ApplyStatusUpdateExecutor.java index fd8e153c..8c249908 100644 --- a/src/main/java/com/gloddy/server/apply/domain/service/ApplyStatusUpdateExecutor.java +++ b/src/main/java/com/gloddy/server/apply/domain/service/ApplyStatusUpdateExecutor.java @@ -3,10 +3,13 @@ import com.gloddy.server.apply.domain.Apply; import com.gloddy.server.apply.domain.handler.ApplyQueryHandler; import com.gloddy.server.apply.domain.vo.Status; +import com.gloddy.server.apply.event.ApplyStatusUpdateEvent; import com.gloddy.server.apply.event.producer.ApplyEventProducer; import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Component; +import static com.gloddy.server.apply.domain.vo.Status.*; + @Component @RequiredArgsConstructor public class ApplyStatusUpdateExecutor { @@ -27,5 +30,7 @@ public void execute(Long userId, Long applyId, Status status) { } else { throw new RuntimeException("Apply Status Invalid"); } + + applyEventProducer.produceEvent(new ApplyStatusUpdateEvent(userId, apply.getGroup().getId(), applyId, status)); } } diff --git a/src/main/java/com/gloddy/server/apply/event/ApplyCreateEvent.java b/src/main/java/com/gloddy/server/apply/event/ApplyCreateEvent.java new file mode 100644 index 00000000..141b6e46 --- /dev/null +++ b/src/main/java/com/gloddy/server/apply/event/ApplyCreateEvent.java @@ -0,0 +1,16 @@ +package com.gloddy.server.apply.event; + +import com.gloddy.server.core.event.Event; +import lombok.AccessLevel; +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.NoArgsConstructor; + +@NoArgsConstructor(access = AccessLevel.PROTECTED) +@AllArgsConstructor +@Getter +public class ApplyCreateEvent implements Event { + private Long userId; + private Long groupId; + private Long applyId; +} diff --git a/src/main/java/com/gloddy/server/apply/event/ApplyStatusUpdateEvent.java b/src/main/java/com/gloddy/server/apply/event/ApplyStatusUpdateEvent.java new file mode 100644 index 00000000..180d91bd --- /dev/null +++ b/src/main/java/com/gloddy/server/apply/event/ApplyStatusUpdateEvent.java @@ -0,0 +1,18 @@ +package com.gloddy.server.apply.event; + +import com.gloddy.server.apply.domain.vo.Status; +import com.gloddy.server.core.event.Event; +import lombok.AccessLevel; +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.NoArgsConstructor; + +@Getter +@NoArgsConstructor(access = AccessLevel.PROTECTED) +@AllArgsConstructor +public class ApplyStatusUpdateEvent implements Event { + private Long userId; + private Long groupId; + private Long applyId; + private Status status; +} diff --git a/src/main/java/com/gloddy/server/group_member/application/GroupMemberDeleteService.java b/src/main/java/com/gloddy/server/group_member/application/GroupMemberDeleteService.java index 0d8c05fe..b680b815 100644 --- a/src/main/java/com/gloddy/server/group_member/application/GroupMemberDeleteService.java +++ b/src/main/java/com/gloddy/server/group_member/application/GroupMemberDeleteService.java @@ -28,7 +28,7 @@ public void delete(Long userId, Long groupId) { deleteGroupMemberVo(userId, groupId); deleteGroupMember(userId, groupId); - groupMemberEventProducer.produceEvent(new GroupMemberLeaveEvent(userId)); + groupMemberEventProducer.produceEvent(new GroupMemberLeaveEvent(userId, groupId)); } private void deleteGroupMemberVo(Long userId, Long groupId) { diff --git a/src/main/java/com/gloddy/server/group_member/event/GroupMemberLeaveEvent.java b/src/main/java/com/gloddy/server/group_member/event/GroupMemberLeaveEvent.java index f1f141ec..86d8e368 100644 --- a/src/main/java/com/gloddy/server/group_member/event/GroupMemberLeaveEvent.java +++ b/src/main/java/com/gloddy/server/group_member/event/GroupMemberLeaveEvent.java @@ -11,4 +11,5 @@ @NoArgsConstructor(access = AccessLevel.PROTECTED) public class GroupMemberLeaveEvent implements Event { private Long userId; + private Long groupId; } diff --git a/src/main/java/com/gloddy/server/messaging/AdapterEvent.java b/src/main/java/com/gloddy/server/messaging/AdapterEvent.java new file mode 100644 index 00000000..648f9748 --- /dev/null +++ b/src/main/java/com/gloddy/server/messaging/AdapterEvent.java @@ -0,0 +1,4 @@ +package com.gloddy.server.messaging; + +public interface AdapterEvent { +} diff --git a/src/main/java/com/gloddy/server/messaging/MessagePublisher.java b/src/main/java/com/gloddy/server/messaging/MessagePublisher.java new file mode 100644 index 00000000..3b0f2632 --- /dev/null +++ b/src/main/java/com/gloddy/server/messaging/MessagePublisher.java @@ -0,0 +1,9 @@ +package com.gloddy.server.messaging; + +import com.gloddy.server.messaging.adapter.apply.event.ApplyAdapterEvent; +import com.gloddy.server.messaging.adapter.group.event.GroupMemberAdapterEvent; + +public interface MessagePublisher { + void publishApplyEvent(ApplyAdapterEvent event); + void publishGroupMemberEvent(GroupMemberAdapterEvent event); +} diff --git a/src/main/java/com/gloddy/server/messaging/adapter/apply/event/ApplyAdapterEvent.java b/src/main/java/com/gloddy/server/messaging/adapter/apply/event/ApplyAdapterEvent.java new file mode 100644 index 00000000..d39c2cb0 --- /dev/null +++ b/src/main/java/com/gloddy/server/messaging/adapter/apply/event/ApplyAdapterEvent.java @@ -0,0 +1,17 @@ +package com.gloddy.server.messaging.adapter.apply.event; + +import com.gloddy.server.messaging.AdapterEvent; +import lombok.AccessLevel; +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.NoArgsConstructor; + +@NoArgsConstructor(access = AccessLevel.PROTECTED) +@AllArgsConstructor +@Getter +public class ApplyAdapterEvent implements AdapterEvent { + private Long userId; + private Long groupId; + private Long applyId; + private ApplyEventType eventType; +} diff --git a/src/main/java/com/gloddy/server/messaging/adapter/apply/event/ApplyEventType.java b/src/main/java/com/gloddy/server/messaging/adapter/apply/event/ApplyEventType.java new file mode 100644 index 00000000..702ed89e --- /dev/null +++ b/src/main/java/com/gloddy/server/messaging/adapter/apply/event/ApplyEventType.java @@ -0,0 +1,14 @@ +package com.gloddy.server.messaging.adapter.apply.event; + +import lombok.AllArgsConstructor; +import lombok.Getter; + +@AllArgsConstructor +@Getter +public enum ApplyEventType { + APPLY_CREATE("지원서가 생성 되었을 때"), + APPLY_APPROVE("지원서가 승인 처리 되었을 때"), + APPLY_REFUSE("지원서가 거절 처리 되었을 때"); + + private final String description; +} diff --git a/src/main/java/com/gloddy/server/messaging/adapter/apply/handler/ApplyAdapterEventHandler.java b/src/main/java/com/gloddy/server/messaging/adapter/apply/handler/ApplyAdapterEventHandler.java new file mode 100644 index 00000000..d8eb912e --- /dev/null +++ b/src/main/java/com/gloddy/server/messaging/adapter/apply/handler/ApplyAdapterEventHandler.java @@ -0,0 +1,30 @@ +package com.gloddy.server.messaging.adapter.apply.handler; + +import com.gloddy.server.apply.event.ApplyCreateEvent; +import com.gloddy.server.apply.event.ApplyStatusUpdateEvent; +import com.gloddy.server.messaging.MessagePublisher; +import com.gloddy.server.messaging.adapter.apply.event.ApplyAdapterEvent; +import com.gloddy.server.messaging.adapter.apply.mapper.ApplyEventMapper; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Component; +import org.springframework.transaction.event.TransactionPhase; +import org.springframework.transaction.event.TransactionalEventListener; + +@Component +@RequiredArgsConstructor +public class ApplyAdapterEventHandler { + + private final MessagePublisher messagePublisher; + + @TransactionalEventListener(phase = TransactionPhase.AFTER_COMMIT) + public void handle(ApplyCreateEvent event) { + ApplyAdapterEvent adapterEvent = ApplyEventMapper.mapToApplyAdapterEventFrom(event); + messagePublisher.publishApplyEvent(adapterEvent); + } + + @TransactionalEventListener(phase = TransactionPhase.AFTER_COMMIT) + public void handle(ApplyStatusUpdateEvent event) { + ApplyAdapterEvent adapterEvent = ApplyEventMapper.mapToApplyAdapterEventFrom(event); + messagePublisher.publishApplyEvent(adapterEvent); + } +} diff --git a/src/main/java/com/gloddy/server/messaging/adapter/apply/mapper/ApplyEventMapper.java b/src/main/java/com/gloddy/server/messaging/adapter/apply/mapper/ApplyEventMapper.java new file mode 100644 index 00000000..a81d28d4 --- /dev/null +++ b/src/main/java/com/gloddy/server/messaging/adapter/apply/mapper/ApplyEventMapper.java @@ -0,0 +1,38 @@ +package com.gloddy.server.messaging.adapter.apply.mapper; + +import com.gloddy.server.apply.domain.vo.Status; +import com.gloddy.server.apply.event.ApplyCreateEvent; +import com.gloddy.server.apply.event.ApplyStatusUpdateEvent; +import com.gloddy.server.messaging.adapter.apply.event.ApplyAdapterEvent; +import com.gloddy.server.messaging.adapter.apply.event.ApplyEventType; + +public class ApplyEventMapper { + + public static ApplyAdapterEvent mapToApplyAdapterEventFrom(ApplyCreateEvent applyCreateEvent) { + return new ApplyAdapterEvent( + applyCreateEvent.getUserId(), + applyCreateEvent.getGroupId(), + applyCreateEvent.getApplyId(), + ApplyEventType.APPLY_CREATE + ); + } + + public static ApplyAdapterEvent mapToApplyAdapterEventFrom(ApplyStatusUpdateEvent applyStatusUpdateEvent) { + return new ApplyAdapterEvent( + applyStatusUpdateEvent.getUserId(), + applyStatusUpdateEvent.getGroupId(), + applyStatusUpdateEvent.getApplyId(), + getApplyEventType(applyStatusUpdateEvent.getStatus()) + ); + } + + private static ApplyEventType getApplyEventType(Status status) { + if (status.isApprove()) { + return ApplyEventType.APPLY_APPROVE; + } else if (status.isRefuse()) { + return ApplyEventType.APPLY_REFUSE; + } else { + throw new RuntimeException("invalid apply status"); + } + } +} diff --git a/src/main/java/com/gloddy/server/messaging/adapter/group/event/GroupMemberAdapterEvent.java b/src/main/java/com/gloddy/server/messaging/adapter/group/event/GroupMemberAdapterEvent.java new file mode 100644 index 00000000..4e0bb7ec --- /dev/null +++ b/src/main/java/com/gloddy/server/messaging/adapter/group/event/GroupMemberAdapterEvent.java @@ -0,0 +1,16 @@ +package com.gloddy.server.messaging.adapter.group.event; + +import com.gloddy.server.messaging.AdapterEvent; +import lombok.AccessLevel; +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.NoArgsConstructor; + +@NoArgsConstructor(access = AccessLevel.PROTECTED) +@AllArgsConstructor +@Getter +public class GroupMemberAdapterEvent implements AdapterEvent { + private Long userId; + private Long groupId; + private GroupMemberEventType eventType; +} diff --git a/src/main/java/com/gloddy/server/messaging/adapter/group/event/GroupMemberEventType.java b/src/main/java/com/gloddy/server/messaging/adapter/group/event/GroupMemberEventType.java new file mode 100644 index 00000000..0516ebff --- /dev/null +++ b/src/main/java/com/gloddy/server/messaging/adapter/group/event/GroupMemberEventType.java @@ -0,0 +1,12 @@ +package com.gloddy.server.messaging.adapter.group.event; + +import lombok.AllArgsConstructor; +import lombok.Getter; + +@AllArgsConstructor +@Getter +public enum GroupMemberEventType { + GROUP_MEMBER_LEAVE("그룹 멤버가 모임을 나갔을 때"); + + private final String description; +} diff --git a/src/main/java/com/gloddy/server/messaging/adapter/group/handler/GroupMemberAdapterEventHandler.java b/src/main/java/com/gloddy/server/messaging/adapter/group/handler/GroupMemberAdapterEventHandler.java new file mode 100644 index 00000000..086b9eca --- /dev/null +++ b/src/main/java/com/gloddy/server/messaging/adapter/group/handler/GroupMemberAdapterEventHandler.java @@ -0,0 +1,26 @@ +package com.gloddy.server.messaging.adapter.group.handler; + +import com.gloddy.server.group_member.event.GroupMemberLeaveEvent; +import com.gloddy.server.messaging.MessagePublisher; +import com.gloddy.server.messaging.adapter.group.event.GroupMemberAdapterEvent; +import com.gloddy.server.messaging.adapter.group.event.GroupMemberEventType; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Component; +import org.springframework.transaction.event.TransactionPhase; +import org.springframework.transaction.event.TransactionalEventListener; + +@Component +@RequiredArgsConstructor +public class GroupMemberAdapterEventHandler { + private final MessagePublisher messagePublisher; + + @TransactionalEventListener(phase = TransactionPhase.AFTER_COMMIT) + public void handle(GroupMemberLeaveEvent event) { + GroupMemberAdapterEvent adapterEvent = new GroupMemberAdapterEvent( + event.getUserId(), + event.getGroupId(), + GroupMemberEventType.GROUP_MEMBER_LEAVE); + + messagePublisher.publishGroupMemberEvent(adapterEvent); + } +} diff --git a/src/main/java/com/gloddy/server/messaging/sns/config/SnsConfig.java b/src/main/java/com/gloddy/server/messaging/sns/config/SnsConfig.java new file mode 100644 index 00000000..969fe3ba --- /dev/null +++ b/src/main/java/com/gloddy/server/messaging/sns/config/SnsConfig.java @@ -0,0 +1,18 @@ +package com.gloddy.server.messaging.sns.config; + +import io.awspring.cloud.sns.core.TopicArnResolver; +import io.awspring.cloud.sns.core.TopicsListingTopicArnResolver; +import org.springframework.boot.context.properties.EnableConfigurationProperties; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import software.amazon.awssdk.services.sns.SnsClient; + +@Configuration +@EnableConfigurationProperties(SnsProperties.class) +public class SnsConfig { + + @Bean + public TopicArnResolver topicArnResolver(SnsClient snsClient) { + return new TopicsListingTopicArnResolver(snsClient); + } +} diff --git a/src/main/java/com/gloddy/server/messaging/sns/config/SnsProperties.java b/src/main/java/com/gloddy/server/messaging/sns/config/SnsProperties.java new file mode 100644 index 00000000..8b95718f --- /dev/null +++ b/src/main/java/com/gloddy/server/messaging/sns/config/SnsProperties.java @@ -0,0 +1,13 @@ +package com.gloddy.server.messaging.sns.config; + +import lombok.Getter; +import lombok.Setter; +import org.springframework.boot.context.properties.ConfigurationProperties; + +@ConfigurationProperties("spring.cloud.event") +@Getter +@Setter +public class SnsProperties { + private String applyTopic; + private String groupMemberTopic; +} diff --git a/src/main/java/com/gloddy/server/messaging/sns/publisher/SnsPublisher.java b/src/main/java/com/gloddy/server/messaging/sns/publisher/SnsPublisher.java new file mode 100644 index 00000000..6181c505 --- /dev/null +++ b/src/main/java/com/gloddy/server/messaging/sns/publisher/SnsPublisher.java @@ -0,0 +1,41 @@ +package com.gloddy.server.messaging.sns.publisher; + +import com.gloddy.server.messaging.AdapterEvent; +import com.gloddy.server.messaging.MessagePublisher; +import com.gloddy.server.messaging.adapter.apply.event.ApplyAdapterEvent; +import com.gloddy.server.messaging.adapter.group.event.GroupMemberAdapterEvent; +import com.gloddy.server.messaging.sns.config.SnsProperties; +import io.awspring.cloud.sns.core.SnsTemplate; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.lang.Nullable; +import org.springframework.stereotype.Component; + +@Component +@Slf4j +@RequiredArgsConstructor +public class SnsPublisher implements MessagePublisher { + + private final SnsProperties snsProperties; + private final SnsTemplate snsTemplate; + + @Override + public void publishApplyEvent(ApplyAdapterEvent event) { + send(snsProperties.getApplyTopic(), event, null); + } + + @Override + public void publishGroupMemberEvent(GroupMemberAdapterEvent event) { + send(snsProperties.getGroupMemberTopic(), event, null); + } + + private void send(String topicName, AdapterEvent event, @Nullable String subject) { + log.info("이벤트 발행 시작(AWS SNS)"); + try { + snsTemplate.sendNotification(topicName, event, subject); + } catch (Exception e) { + log.error(e.getMessage()); + } + log.info("이벤트 발행 완료(AWS SNS)"); + } +} diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml index 79f96a2b..07710c8b 100644 --- a/src/main/resources/application.yml +++ b/src/main/resources/application.yml @@ -43,6 +43,17 @@ spring: max-file-size: ${MAX_FILE_SIZE} max-request-size: ${MAX_REQUEST_SIZE} + cloud: + aws: + credentials: + access-key: ${AWS_ACCESS_KEY} + secret-key: ${AWS_SECRET_KEY} + region: + static: ap-northeast-2 + event: + apply-topic: ${APPLY_TOPIC} + group-member-topic: ${GROUP_MEMBER_TOPIC} + logging: level: com.gloddy.server.authSms.infra: debug @@ -79,4 +90,3 @@ springdoc: swagger-ui: url: /v3/api-docs urls-primary-name: Gloddy - diff --git a/src/test/resources/application.yml b/src/test/resources/application.yml index 53d21b3a..fe88bcc3 100644 --- a/src/test/resources/application.yml +++ b/src/test/resources/application.yml @@ -31,6 +31,17 @@ spring: pathmatch: matching-strategy: ant_path_matcher + cloud: + aws: + credentials: + access-key: dummy-key + secret-key: dummy-key + region: + static: ap-northeast-2 + event: + apply-topic: dummy-apply-topic + group-member-topic: dummy-group-member-topic + jwt: header: X-AUTH-TOKEN secret: testJwtSecret-testJwtSecret-testJwtSecret-testJwtSecret-testJwtSecret-testJwtSecret