From 0681d65168e11e687ad6e492034e5e33337ee050 Mon Sep 17 00:00:00 2001 From: Parkjyun <98092394+Parkjyun@users.noreply.github.com> Date: Tue, 15 Oct 2024 23:00:08 +0900 Subject: [PATCH] =?UTF-8?q?[feat]=20=EB=94=94=EC=8A=A4=EC=BD=94=EB=93=9C?= =?UTF-8?q?=20=EC=95=8C=EB=9E=8C=20(#206)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * [feat] create discord feign client * [feat] create DiscordService * [feat] create eventpublisher interface and impl * [feat] create discord event listener * [feat] add event publisher in logic * [feat] add discord webhook in yml * [refac] refactor parameters * [refac] delete unused import statement * [refac] fix typo * [feat] add userid to discord message on store delete * [chore] add gitignore * [dependency] add discord appender dependency * [feat] change EventListener to TransactionalEventListener * [dependency] edit build.gradle --- build.gradle | 4 +++ .../api/auth/service/AuthService.java | 4 +++ .../service/DiscordEventListener.java | 31 +++++++++++++++++++ .../api/external/service/DiscordService.java | 23 ++++++++++++++ .../api/store/controller/StoreController.java | 9 ++---- .../store/service/StoreCommandService.java | 14 ++++++--- .../service/command/StoreDeleteCommand.java | 8 +++++ .../hankkiserver/event/EventPublisher.java | 5 +++ .../event/EventPublisherAdapter.java | 17 ++++++++++ .../event/store/CreateStoreEvent.java | 8 +++++ .../event/store/DeleteStoreEvent.java | 7 +++++ .../event/user/CreateUserEvent.java | 11 +++++++ .../openfeign/discord/DiscordFeignClient.java | 19 ++++++++++++ .../openfeign/discord/dto/DiscordMessage.java | 15 +++++++++ 14 files changed, 165 insertions(+), 10 deletions(-) create mode 100644 src/main/java/org/hankki/hankkiserver/api/external/service/DiscordEventListener.java create mode 100644 src/main/java/org/hankki/hankkiserver/api/external/service/DiscordService.java create mode 100644 src/main/java/org/hankki/hankkiserver/api/store/service/command/StoreDeleteCommand.java create mode 100644 src/main/java/org/hankki/hankkiserver/event/EventPublisher.java create mode 100644 src/main/java/org/hankki/hankkiserver/event/EventPublisherAdapter.java create mode 100644 src/main/java/org/hankki/hankkiserver/event/store/CreateStoreEvent.java create mode 100644 src/main/java/org/hankki/hankkiserver/event/store/DeleteStoreEvent.java create mode 100644 src/main/java/org/hankki/hankkiserver/event/user/CreateUserEvent.java create mode 100644 src/main/java/org/hankki/hankkiserver/external/openfeign/discord/DiscordFeignClient.java create mode 100644 src/main/java/org/hankki/hankkiserver/external/openfeign/discord/dto/DiscordMessage.java diff --git a/build.gradle b/build.gradle index 4d4bc95b..e8ec5591 100644 --- a/build.gradle +++ b/build.gradle @@ -21,6 +21,7 @@ configurations { repositories { mavenCentral() + maven { url 'https://jitpack.io' } } dependencies { @@ -68,6 +69,9 @@ dependencies { annotationProcessor "com.querydsl:querydsl-apt:${dependencyManagement.importedProperties['querydsl.version']}:jakarta" annotationProcessor "jakarta.annotation:jakarta.annotation-api" annotationProcessor "jakarta.persistence:jakarta.persistence-api" + + // Discord Webhook + implementation 'com.github.napstr:logback-discord-appender:1.0.0' } tasks.named('test') { diff --git a/src/main/java/org/hankki/hankkiserver/api/auth/service/AuthService.java b/src/main/java/org/hankki/hankkiserver/api/auth/service/AuthService.java index 84591a0a..fbee76ea 100644 --- a/src/main/java/org/hankki/hankkiserver/api/auth/service/AuthService.java +++ b/src/main/java/org/hankki/hankkiserver/api/auth/service/AuthService.java @@ -14,6 +14,8 @@ import org.hankki.hankkiserver.domain.user.model.User; import org.hankki.hankkiserver.domain.user.model.UserInfo; import org.hankki.hankkiserver.domain.user.model.UserStatus; +import org.hankki.hankkiserver.event.EventPublisher; +import org.hankki.hankkiserver.event.user.CreateUserEvent; import org.hankki.hankkiserver.external.openfeign.apple.AppleClientSecretGenerator; import org.hankki.hankkiserver.external.openfeign.apple.AppleOAuthProvider; import org.hankki.hankkiserver.external.openfeign.dto.SocialInfoDto; @@ -48,6 +50,7 @@ public class AuthService { private final KakaoOAuthProvider kakaoOAuthProvider; private final AppleOAuthProvider appleOAuthProvider; private final AppleClientSecretGenerator appleClientSecretGenerator; + private final EventPublisher eventPublisher; public UserLoginResponse login(final String token, final UserLoginRequest request) { Platform platform = Platform.getEnumPlatformFromStringPlatform(request.platform()); @@ -114,6 +117,7 @@ private User loadOrCreateUser(final Optional findUser, final Platform plat .orElseGet(() -> { User newUser = createUser(socialInfo.name(), socialInfo.email(), socialInfo.serialId(), platform); saveUserAndUserInfo(newUser); + eventPublisher.publish(CreateUserEvent.of(newUser.getId(), newUser.getName(), newUser.getPlatform().toString())); return newUser; }); } diff --git a/src/main/java/org/hankki/hankkiserver/api/external/service/DiscordEventListener.java b/src/main/java/org/hankki/hankkiserver/api/external/service/DiscordEventListener.java new file mode 100644 index 00000000..f4eb0f34 --- /dev/null +++ b/src/main/java/org/hankki/hankkiserver/api/external/service/DiscordEventListener.java @@ -0,0 +1,31 @@ +package org.hankki.hankkiserver.api.external.service; + +import lombok.RequiredArgsConstructor; +import org.hankki.hankkiserver.event.store.CreateStoreEvent; +import org.hankki.hankkiserver.event.store.DeleteStoreEvent; +import org.hankki.hankkiserver.event.user.CreateUserEvent; +import org.springframework.stereotype.Component; +import org.springframework.transaction.event.TransactionPhase; +import org.springframework.transaction.event.TransactionalEventListener; + +@Component +@RequiredArgsConstructor +public class DiscordEventListener { + + private final DiscordService discordService; + + @TransactionalEventListener(phase = TransactionPhase.AFTER_COMMIT) + public void sendStoreCreationNotice(CreateStoreEvent event) { + discordService.sendStoreCreationMessage(event.storeName(), event.universityName()); + } + + @TransactionalEventListener(phase = TransactionPhase.AFTER_COMMIT) + public void sendUserCreateNotice(CreateUserEvent event) { + discordService.sendUserCreationMessage(event.userId(), event.userName(), event.platform()); + } + + @TransactionalEventListener(phase = TransactionPhase.AFTER_COMMIT) + public void sendStoreDeleteNotice(DeleteStoreEvent event) { + discordService.sendStoreDeleteMessage(event.name(), event.userId()); + } +} diff --git a/src/main/java/org/hankki/hankkiserver/api/external/service/DiscordService.java b/src/main/java/org/hankki/hankkiserver/api/external/service/DiscordService.java new file mode 100644 index 00000000..28522bed --- /dev/null +++ b/src/main/java/org/hankki/hankkiserver/api/external/service/DiscordService.java @@ -0,0 +1,23 @@ +package org.hankki.hankkiserver.api.external.service; + +import lombok.RequiredArgsConstructor; +import org.hankki.hankkiserver.external.openfeign.discord.DiscordFeignClient; +import org.hankki.hankkiserver.external.openfeign.discord.dto.DiscordMessage; +import org.springframework.stereotype.Service; + +@Service +@RequiredArgsConstructor +public class DiscordService { + + private final DiscordFeignClient discordFeignClient; + + public void sendStoreCreationMessage(String storeName, String universityName) { + discordFeignClient.sendStoreCreationMessage(DiscordMessage.storeCreationMessageOf(storeName, universityName)); + } + public void sendUserCreationMessage(Long userId, String userName, String platform) { + discordFeignClient.sendUserCreationMessage(DiscordMessage.userCreationMessageOf(userId, userName, platform)); + } + public void sendStoreDeleteMessage(String storeName, Long userId) { + discordFeignClient.sendStoreDeleteMessage(DiscordMessage.storeDeleteMessageOf(storeName, userId)); + } +} diff --git a/src/main/java/org/hankki/hankkiserver/api/store/controller/StoreController.java b/src/main/java/org/hankki/hankkiserver/api/store/controller/StoreController.java index eb05d1fe..44777968 100644 --- a/src/main/java/org/hankki/hankkiserver/api/store/controller/StoreController.java +++ b/src/main/java/org/hankki/hankkiserver/api/store/controller/StoreController.java @@ -10,10 +10,7 @@ import org.hankki.hankkiserver.api.store.service.HeartCommandService; import org.hankki.hankkiserver.api.store.service.StoreCommandService; import org.hankki.hankkiserver.api.store.service.StoreQueryService; -import org.hankki.hankkiserver.api.store.service.command.HeartDeleteCommand; -import org.hankki.hankkiserver.api.store.service.command.HeartPostCommand; -import org.hankki.hankkiserver.api.store.service.command.StorePostCommand; -import org.hankki.hankkiserver.api.store.service.command.StoreValidationCommand; +import org.hankki.hankkiserver.api.store.service.command.*; import org.hankki.hankkiserver.api.store.service.response.*; import org.hankki.hankkiserver.auth.UserId; import org.hankki.hankkiserver.common.code.CommonSuccessCode; @@ -94,8 +91,8 @@ public HankkiResponse createStore(@RequestPart(required = fal } @DeleteMapping("/stores/{id}") - public HankkiResponse deleteStore(@PathVariable final Long id) { - storeCommandService.deleteStore(id); + public HankkiResponse deleteStore(@PathVariable final Long id, @UserId final Long userId) { + storeCommandService.deleteStore(StoreDeleteCommand.of(id, userId)); return HankkiResponse.success(CommonSuccessCode.NO_CONTENT); } } diff --git a/src/main/java/org/hankki/hankkiserver/api/store/service/StoreCommandService.java b/src/main/java/org/hankki/hankkiserver/api/store/service/StoreCommandService.java index 880bb97b..46e070d9 100644 --- a/src/main/java/org/hankki/hankkiserver/api/store/service/StoreCommandService.java +++ b/src/main/java/org/hankki/hankkiserver/api/store/service/StoreCommandService.java @@ -2,6 +2,8 @@ import lombok.RequiredArgsConstructor; import org.hankki.hankkiserver.api.auth.service.UserFinder; +import org.hankki.hankkiserver.api.store.service.command.StoreDeleteCommand; +import org.hankki.hankkiserver.event.store.CreateStoreEvent; import org.hankki.hankkiserver.api.menu.service.MenuUpdater; import org.hankki.hankkiserver.api.report.service.ReportUpdater; import org.hankki.hankkiserver.api.store.service.command.StorePostCommand; @@ -18,6 +20,8 @@ import org.hankki.hankkiserver.domain.store.model.StoreImage; import org.hankki.hankkiserver.domain.university.model.University; import org.hankki.hankkiserver.domain.universitystore.model.UniversityStore; +import org.hankki.hankkiserver.event.EventPublisher; +import org.hankki.hankkiserver.event.store.DeleteStoreEvent; import org.hankki.hankkiserver.external.s3.S3Service; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; @@ -40,20 +44,20 @@ public class StoreCommandService { private final ReportUpdater reportUpdater; private final UserFinder userFinder; private final StoreFinder storeFinder; + private final EventPublisher publisher; @Transactional(rollbackFor = Exception.class) public StorePostResponse createStore(final StorePostCommand command) { if (storeExists(command.latitude(), command.longitude(), command.name(), false)) { throw new BadRequestException(StoreErrorCode.BAD_STORE_INFO); } - Store store = storeUpdater.save(command.toEntity()); saveImages(command, store); menuUpdater.saveAll(getMenus(command, store)); - University university = universityFinder.findById(command.universityId()); universityStoreUpdater.save(UniversityStore.create(store, university)); reportUpdater.save(Report.create(userFinder.getUser(command.userId()), store, university)); + publisher.publish(CreateStoreEvent.of(store.getName(), university.getName())); return StorePostResponse.of(store); } @@ -84,7 +88,9 @@ private List getMenus(final StorePostCommand command, final Store store) { } @Transactional - public void deleteStore(final Long id) { - storeFinder.findByIdWhereDeletedIsFalse(id).softDelete(); + public void deleteStore(final StoreDeleteCommand command) { + Store findStore = storeFinder.findByIdWhereDeletedIsFalse(command.storeId()); + findStore.softDelete(); + publisher.publish(DeleteStoreEvent.of(findStore.getName(), command.userId())); } } diff --git a/src/main/java/org/hankki/hankkiserver/api/store/service/command/StoreDeleteCommand.java b/src/main/java/org/hankki/hankkiserver/api/store/service/command/StoreDeleteCommand.java new file mode 100644 index 00000000..2e0a02c2 --- /dev/null +++ b/src/main/java/org/hankki/hankkiserver/api/store/service/command/StoreDeleteCommand.java @@ -0,0 +1,8 @@ +package org.hankki.hankkiserver.api.store.service.command; + +public record StoreDeleteCommand(Long storeId, + Long userId) { + public static StoreDeleteCommand of(Long storeId, Long userId) { + return new StoreDeleteCommand(storeId, userId); + } +} diff --git a/src/main/java/org/hankki/hankkiserver/event/EventPublisher.java b/src/main/java/org/hankki/hankkiserver/event/EventPublisher.java new file mode 100644 index 00000000..17c0c003 --- /dev/null +++ b/src/main/java/org/hankki/hankkiserver/event/EventPublisher.java @@ -0,0 +1,5 @@ +package org.hankki.hankkiserver.event; + +public interface EventPublisher { + void publish(Object event); +} diff --git a/src/main/java/org/hankki/hankkiserver/event/EventPublisherAdapter.java b/src/main/java/org/hankki/hankkiserver/event/EventPublisherAdapter.java new file mode 100644 index 00000000..3bd6bd8f --- /dev/null +++ b/src/main/java/org/hankki/hankkiserver/event/EventPublisherAdapter.java @@ -0,0 +1,17 @@ +package org.hankki.hankkiserver.event; + +import lombok.RequiredArgsConstructor; +import org.springframework.context.ApplicationEventPublisher; +import org.springframework.stereotype.Component; + +@Component +@RequiredArgsConstructor +public class EventPublisherAdapter implements EventPublisher { + + private final ApplicationEventPublisher applicationEventPublisher; + + @Override + public void publish(Object event) { + applicationEventPublisher.publishEvent(event); + } +} diff --git a/src/main/java/org/hankki/hankkiserver/event/store/CreateStoreEvent.java b/src/main/java/org/hankki/hankkiserver/event/store/CreateStoreEvent.java new file mode 100644 index 00000000..b96f4b54 --- /dev/null +++ b/src/main/java/org/hankki/hankkiserver/event/store/CreateStoreEvent.java @@ -0,0 +1,8 @@ +package org.hankki.hankkiserver.event.store; + +public record CreateStoreEvent (String storeName, + String universityName) { + public static CreateStoreEvent of(String storeName, String universityName) { + return new CreateStoreEvent(storeName, universityName); + } +} diff --git a/src/main/java/org/hankki/hankkiserver/event/store/DeleteStoreEvent.java b/src/main/java/org/hankki/hankkiserver/event/store/DeleteStoreEvent.java new file mode 100644 index 00000000..1bdd442f --- /dev/null +++ b/src/main/java/org/hankki/hankkiserver/event/store/DeleteStoreEvent.java @@ -0,0 +1,7 @@ +package org.hankki.hankkiserver.event.store; + +public record DeleteStoreEvent(String name, Long userId) { + public static DeleteStoreEvent of(String name, Long userId) { + return new DeleteStoreEvent(name, userId); + } +} diff --git a/src/main/java/org/hankki/hankkiserver/event/user/CreateUserEvent.java b/src/main/java/org/hankki/hankkiserver/event/user/CreateUserEvent.java new file mode 100644 index 00000000..9d0a5d41 --- /dev/null +++ b/src/main/java/org/hankki/hankkiserver/event/user/CreateUserEvent.java @@ -0,0 +1,11 @@ +package org.hankki.hankkiserver.event.user; + +public record CreateUserEvent( + Long userId, + String userName, + String platform +) { + public static CreateUserEvent of(Long userId, String userName, String platform) { + return new CreateUserEvent(userId, userName, platform); + } +} diff --git a/src/main/java/org/hankki/hankkiserver/external/openfeign/discord/DiscordFeignClient.java b/src/main/java/org/hankki/hankkiserver/external/openfeign/discord/DiscordFeignClient.java new file mode 100644 index 00000000..77644848 --- /dev/null +++ b/src/main/java/org/hankki/hankkiserver/external/openfeign/discord/DiscordFeignClient.java @@ -0,0 +1,19 @@ +package org.hankki.hankkiserver.external.openfeign.discord; + +import org.hankki.hankkiserver.external.openfeign.discord.dto.DiscordMessage; +import org.springframework.cloud.openfeign.FeignClient; +import org.springframework.http.MediaType; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; + +@FeignClient(name = "discordClient", url = "${discord.webhook.url}") +public interface DiscordFeignClient { + @PostMapping(value = "${discord.webhook.create-store}", produces = MediaType.APPLICATION_JSON_VALUE) + void sendStoreCreationMessage(@RequestBody DiscordMessage message); + + @PostMapping(value = "${discord.webhook.create-user}", produces = MediaType.APPLICATION_JSON_VALUE) + void sendUserCreationMessage(@RequestBody DiscordMessage message); + + @PostMapping(value = "${discord.webhook.delete-store}", produces = MediaType.APPLICATION_JSON_VALUE) + void sendStoreDeleteMessage(@RequestBody DiscordMessage message); +} diff --git a/src/main/java/org/hankki/hankkiserver/external/openfeign/discord/dto/DiscordMessage.java b/src/main/java/org/hankki/hankkiserver/external/openfeign/discord/dto/DiscordMessage.java new file mode 100644 index 00000000..923bf7ef --- /dev/null +++ b/src/main/java/org/hankki/hankkiserver/external/openfeign/discord/dto/DiscordMessage.java @@ -0,0 +1,15 @@ +package org.hankki.hankkiserver.external.openfeign.discord.dto; + +public record DiscordMessage(String content) { + public static DiscordMessage storeCreationMessageOf(String storeName, String universityName) { + return new DiscordMessage(universityName+ "에 <" +storeName+ ">가 생성되었습니다."); + } + + public static DiscordMessage userCreationMessageOf(Long userId, String userName, String platform) { + return new DiscordMessage( platform + userId + "번째 유저 <" +userName+ ">가 가입했습니다."); + } + + public static DiscordMessage storeDeleteMessageOf(String storeName, Long userId) { + return new DiscordMessage( userId + "번 유저에의해 <" + storeName + ">가 삭제되었습니다."); + } +}