From 9dea6a94cdbf78e99c909bbe85ae27804fdadd5b Mon Sep 17 00:00:00 2001 From: Donghun Shin Date: Fri, 1 Mar 2024 20:24:00 +0900 Subject: [PATCH] =?UTF-8?q?[MOA-487]=20=EC=99=B8=EB=B6=80=20API=20?= =?UTF-8?q?=ED=98=B8=EC=B6=9C=20=EC=8B=A4=ED=8C=A8=EC=8B=9C=20=EB=94=94?= =?UTF-8?q?=EC=8A=A4=EC=BD=94=EB=93=9C=EC=97=90=20=EC=95=8C=EB=A6=BC=20(#1?= =?UTF-8?q?08)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * MOA-487: 외부 API 호출 실패시 디스코드에 알림 * MOA-487: 서브모듈 업데이트 * MOA-487: oauth api 호출 실패시에도 웹훅 알림 --- MoA-Backend-Submodule | 2 +- .../client/discord/DiscordWebHookClient.java | 18 +++++++++++++++--- .../client/discord/DiscordWebHookProperty.java | 9 +++++++-- .../client/oauth/apple/AppleClientConfig.java | 4 ++++ .../client/oauth/kakao/KakaoClientConfig.java | 4 ++++ .../client/oauth/naver/NaverClientConfig.java | 4 ++++ .../java/moa/client/sms/NHNClientConfig.java | 4 ++++ .../main/java/moa/client/sms/SmsClient.java | 9 ++++++--- .../client/toss/TossPaymentClientConfig.java | 4 ++++ .../java/moa/client/wincube/WincubeClient.java | 8 +++++++- .../client/wincube/auth/WincubeAuthClient.java | 6 +++++- .../PersonalInquiryEventHandler.java | 5 +---- .../main/resources/application-core-local.yml | 8 ++++++-- 13 files changed, 68 insertions(+), 17 deletions(-) diff --git a/MoA-Backend-Submodule b/MoA-Backend-Submodule index 02cd8e9d..99761a36 160000 --- a/MoA-Backend-Submodule +++ b/MoA-Backend-Submodule @@ -1 +1 @@ -Subproject commit 02cd8e9d9c14bc7df4a3309486b5d7ab93bd098d +Subproject commit 99761a36561cf090464bbbb297c8dbba30f80353 diff --git a/core/src/main/java/moa/client/discord/DiscordWebHookClient.java b/core/src/main/java/moa/client/discord/DiscordWebHookClient.java index 7bdb7913..36c5ce39 100644 --- a/core/src/main/java/moa/client/discord/DiscordWebHookClient.java +++ b/core/src/main/java/moa/client/discord/DiscordWebHookClient.java @@ -1,20 +1,32 @@ package moa.client.discord; +import static moa.global.config.async.AsyncConfig.VIRTUAL_THREAD_EXECUTOR; + import lombok.RequiredArgsConstructor; import moa.client.discord.dto.DiscordSendMessageRequest; +import org.springframework.scheduling.annotation.Async; import org.springframework.stereotype.Component; @Component @RequiredArgsConstructor +@Async(VIRTUAL_THREAD_EXECUTOR) public class DiscordWebHookClient { private final DiscordWebHookProperty discordWebHookProperty; private final DiscordWebHookApiClient webHookApiClient; - public void sendContent(String content) { + public void sendToInquiryChannel(String content) { + webHookApiClient.sendMessage( + discordWebHookProperty.inquiryChannel().webhookId(), + discordWebHookProperty.inquiryChannel().webhookToken(), + new DiscordSendMessageRequest(content) + ); + } + + public void sendToErrorChannel(String content) { webHookApiClient.sendMessage( - discordWebHookProperty.webhookId(), - discordWebHookProperty.webhookToken(), + discordWebHookProperty.errorChannel().webhookId(), + discordWebHookProperty.errorChannel().webhookToken(), new DiscordSendMessageRequest(content) ); } diff --git a/core/src/main/java/moa/client/discord/DiscordWebHookProperty.java b/core/src/main/java/moa/client/discord/DiscordWebHookProperty.java index 65068cd1..4a16d6e3 100644 --- a/core/src/main/java/moa/client/discord/DiscordWebHookProperty.java +++ b/core/src/main/java/moa/client/discord/DiscordWebHookProperty.java @@ -4,7 +4,12 @@ @ConfigurationProperties(prefix = "discord") public record DiscordWebHookProperty( - String webhookId, - String webhookToken + WebhookProperty inquiryChannel, + WebhookProperty errorChannel ) { + public record WebhookProperty( + String webhookId, + String webhookToken + ) { + } } diff --git a/core/src/main/java/moa/client/oauth/apple/AppleClientConfig.java b/core/src/main/java/moa/client/oauth/apple/AppleClientConfig.java index 93bf7693..f9a52245 100644 --- a/core/src/main/java/moa/client/oauth/apple/AppleClientConfig.java +++ b/core/src/main/java/moa/client/oauth/apple/AppleClientConfig.java @@ -4,6 +4,7 @@ import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; +import moa.client.discord.DiscordWebHookClient; import moa.client.exception.ExternalApiException; import moa.global.http.HttpInterfaceUtil; import org.springframework.context.annotation.Bean; @@ -17,12 +18,15 @@ @RequiredArgsConstructor public class AppleClientConfig { + private final DiscordWebHookClient discordWebHookClient; + @Bean public AppleApiClient appleApiClient() { RestClient restClient = RestClient.builder() .defaultStatusHandler(HttpStatusCode::isError, (request, response) -> { String responseData = new String(response.getBody().readAllBytes()); log.error("Apple API ERROR {}", responseData); + discordWebHookClient.sendToErrorChannel("Apple API ERROR \n ->" + responseData); throw new ExternalApiException(EXTERNAL_API_EXCEPTION .withDetail(responseData) .setStatus(HttpStatus.valueOf(response.getStatusCode().value()))); diff --git a/core/src/main/java/moa/client/oauth/kakao/KakaoClientConfig.java b/core/src/main/java/moa/client/oauth/kakao/KakaoClientConfig.java index 409ff977..2babebfb 100644 --- a/core/src/main/java/moa/client/oauth/kakao/KakaoClientConfig.java +++ b/core/src/main/java/moa/client/oauth/kakao/KakaoClientConfig.java @@ -5,6 +5,7 @@ import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; +import moa.client.discord.DiscordWebHookClient; import moa.client.exception.ExternalApiException; import moa.global.http.HttpInterfaceUtil; import org.springframework.context.annotation.Bean; @@ -18,12 +19,15 @@ @RequiredArgsConstructor public class KakaoClientConfig { + private final DiscordWebHookClient discordWebHookClient; + @Bean public KakaoApiClient kakaoApiClient() { RestClient restClient = RestClient.builder() .defaultStatusHandler(HttpStatusCode::isError, (request, response) -> { String responseData = new String(response.getBody().readAllBytes()); log.error("Kakao API ERROR {}", responseData); + discordWebHookClient.sendToErrorChannel("Kakao API ERROR \n ->" + responseData); throw new ExternalApiException(EXTERNAL_API_EXCEPTION .withDetail(responseData) .setStatus(HttpStatus.valueOf(response.getStatusCode().value()))); diff --git a/core/src/main/java/moa/client/oauth/naver/NaverClientConfig.java b/core/src/main/java/moa/client/oauth/naver/NaverClientConfig.java index c9d8fce3..48670488 100644 --- a/core/src/main/java/moa/client/oauth/naver/NaverClientConfig.java +++ b/core/src/main/java/moa/client/oauth/naver/NaverClientConfig.java @@ -5,6 +5,7 @@ import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; +import moa.client.discord.DiscordWebHookClient; import moa.client.exception.ExternalApiException; import moa.global.http.HttpInterfaceUtil; import org.springframework.context.annotation.Bean; @@ -18,12 +19,15 @@ @RequiredArgsConstructor public class NaverClientConfig { + private final DiscordWebHookClient discordWebHookClient; + @Bean public NaverApiClient naverApiClient() { RestClient restClient = RestClient.builder() .defaultStatusHandler(HttpStatusCode::isError, (request, response) -> { String responseData = new String(response.getBody().readAllBytes()); log.error("Naver API ERROR {}", responseData); + discordWebHookClient.sendToErrorChannel("Naver API ERROR \n ->" + responseData); throw new ExternalApiException(EXTERNAL_API_EXCEPTION .withDetail(responseData) .setStatus(HttpStatus.valueOf(response.getStatusCode().value()))); diff --git a/core/src/main/java/moa/client/sms/NHNClientConfig.java b/core/src/main/java/moa/client/sms/NHNClientConfig.java index 359fadc5..0a1e8471 100644 --- a/core/src/main/java/moa/client/sms/NHNClientConfig.java +++ b/core/src/main/java/moa/client/sms/NHNClientConfig.java @@ -5,6 +5,7 @@ import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; +import moa.client.discord.DiscordWebHookClient; import moa.client.exception.ExternalApiException; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @@ -17,12 +18,15 @@ @RequiredArgsConstructor public class NHNClientConfig { + private final DiscordWebHookClient discordWebHookClient; + @Bean public NHNApiClient nhnApiClient() { RestClient restClient = RestClient.builder() .defaultStatusHandler(HttpStatusCode::isError, (request, response) -> { String responseData = new String(response.getBody().readAllBytes()); log.error("NHN SMS API ERROR {}", responseData); + discordWebHookClient.sendToErrorChannel("NHN SMS API ERROR \n ->" + responseData); throw new ExternalApiException(EXTERNAL_API_EXCEPTION .withDetail(responseData) .setStatus(HttpStatus.valueOf(response.getStatusCode().value()))); diff --git a/core/src/main/java/moa/client/sms/SmsClient.java b/core/src/main/java/moa/client/sms/SmsClient.java index 61677814..1b8b190f 100644 --- a/core/src/main/java/moa/client/sms/SmsClient.java +++ b/core/src/main/java/moa/client/sms/SmsClient.java @@ -3,6 +3,7 @@ import java.util.List; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; +import moa.client.discord.DiscordWebHookClient; import moa.client.sms.NHNSendSmsRequest.RecipientRequest; import org.springframework.stereotype.Service; @@ -13,6 +14,7 @@ public class SmsClient { private final NHNApiClient client; private final NHNSmsConfig config; + private final DiscordWebHookClient discordWebHookClient; public void send(String message, String phoneNumber) { NHNSendSmsRequest request = new NHNSendSmsRequest( @@ -22,11 +24,12 @@ public void send(String message, String phoneNumber) { ); NHNSendSmsResponse response = client.sendSms(config.appKey(), config.secretKey(), request); - if (!response.header().isSuccessful()) { + if (response.header().isSuccessful()) { + log.info("NHN 문자 발송 성공 {}", response); + } else { log.error("NHN 문자 발송 API 에서 문제 발생 {}", response); + discordWebHookClient.sendToErrorChannel("NHN 문자 발송 API 에서 문제 발생 \n ->" + response); throw new RuntimeException("NHN 문자 발송 API 에서 문제 발생. Detail: %s".formatted(response.toString())); - } else { - log.info("NHN 문자 발송 성공 {}", response); } } } diff --git a/core/src/main/java/moa/client/toss/TossPaymentClientConfig.java b/core/src/main/java/moa/client/toss/TossPaymentClientConfig.java index 4f39667d..cc107d5b 100644 --- a/core/src/main/java/moa/client/toss/TossPaymentClientConfig.java +++ b/core/src/main/java/moa/client/toss/TossPaymentClientConfig.java @@ -5,6 +5,7 @@ import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; +import moa.client.discord.DiscordWebHookClient; import moa.client.exception.ExternalApiException; import moa.global.http.HttpInterfaceUtil; import org.springframework.context.annotation.Bean; @@ -20,12 +21,15 @@ @RequiredArgsConstructor public class TossPaymentClientConfig { + private final DiscordWebHookClient discordWebHookClient; + @Bean public TossApiClient tossApiClient() { RestClient build = RestClient.builder() .defaultStatusHandler(HttpStatusCode::isError, (request, response) -> { String responseData = new String(response.getBody().readAllBytes()); log.error("Toss API ERROR {}", responseData); + discordWebHookClient.sendToErrorChannel("Toss API ERROR \n ->" + responseData); throw new ExternalApiException(EXTERNAL_API_EXCEPTION .withDetail(responseData) .setStatus(HttpStatus.valueOf(response.getStatusCode().value()))); diff --git a/core/src/main/java/moa/client/wincube/WincubeClient.java b/core/src/main/java/moa/client/wincube/WincubeClient.java index f9db2356..a9f15ae5 100644 --- a/core/src/main/java/moa/client/wincube/WincubeClient.java +++ b/core/src/main/java/moa/client/wincube/WincubeClient.java @@ -6,6 +6,7 @@ import jakarta.annotation.Nullable; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; +import moa.client.discord.DiscordWebHookClient; import moa.client.exception.ExternalApiException; import moa.client.wincube.auth.WincubeAuthClient; import moa.client.wincube.dto.WincubeCancelCouponResponse; @@ -26,6 +27,7 @@ public class WincubeClient { private final WincubeProperty wincubeProperty; private final WincubeAuthClient authClient; private final WincubeApiClient client; + private final DiscordWebHookClient discordWebHookClient; public WincubeProductResponse getProductList() { String authToken = authClient.getAuthToken(); @@ -63,6 +65,8 @@ private void validateIssueCoupon(WincubeIssueCouponResponse response) { if (response.isSuccess()) { log.info("윈큐브 쿠폰 발행 완료 {}", response); } else { + log.error("윈큐브 쿠폰 발행 실패 {}", response); + discordWebHookClient.sendToErrorChannel("윈큐브 쿠폰 발행 실패 \n ->" + response); throw new ExternalApiException(EXTERNAL_API_EXCEPTION.withDetail( "윈큐브 쿠폰 발행 실패: " + response )); @@ -79,8 +83,10 @@ public void cancelCoupon(String transactionId) { ); log.info("윈큐브 쿠폰 취소 API 호출 완료."); if (!response.isSuccess()) { + log.error("윈큐브 쿠폰 취소 실패 {}", response); + discordWebHookClient.sendToErrorChannel("윈큐브 쿠폰 취소 실패 \n ->" + response); throw new ExternalApiException(EXTERNAL_API_EXCEPTION.withDetail( - "윈큐브 쿠폰 취소 실해: " + response + "윈큐브 쿠폰 취소 실패: " + response )); } } diff --git a/core/src/main/java/moa/client/wincube/auth/WincubeAuthClient.java b/core/src/main/java/moa/client/wincube/auth/WincubeAuthClient.java index b18924fc..2d3ee26c 100644 --- a/core/src/main/java/moa/client/wincube/auth/WincubeAuthClient.java +++ b/core/src/main/java/moa/client/wincube/auth/WincubeAuthClient.java @@ -7,6 +7,7 @@ import com.fasterxml.jackson.databind.ObjectMapper; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; +import moa.client.discord.DiscordWebHookClient; import moa.client.exception.ExternalApiException; import moa.client.wincube.auth.request.WincubeAuthResultCode; import moa.client.wincube.auth.request.WincubeIssueAuthCodeRequest; @@ -25,6 +26,7 @@ public class WincubeAuthClient { private static final int AES_IV_BYTE = 16; + private final DiscordWebHookClient discordWebHookClient; private final ObjectMapper objectMapper; private final WincubeAuthProperty wincubeProperty; private final WincubeAuthApiClient wincubeAuthApiClient; @@ -63,7 +65,8 @@ private void validateResponseIsSuccess(String response) { log.info("윈큐브 Auth Code 응답 검증"); var code = readValue(response, WincubeAuthResultCode.class); if (code.resultCode() >= 400) { - log.error("Wincube AUTH API error {}", response); + log.error("Wincube AUTH CODE API ERROR {}", response); + discordWebHookClient.sendToErrorChannel("Wincube AUTH CODE API ERROR \n ->" + response); throw new ExternalApiException(EXTERNAL_API_EXCEPTION .withDetail(response) .setStatus(HttpStatus.valueOf(code.resultCode()))); @@ -105,6 +108,7 @@ private String getAuthToken(String codeId, String aesIv) { private void validateTokenResponse(WincubeIssueAuthTokenResponse response, String aesIv) { if (response.resultCode() != 200) { log.error("Wincube AUTH TOKEN API ERROR {}", response); + discordWebHookClient.sendToErrorChannel("Wincube AUTH TOKEN API ERROR \n ->" + response); throw new ExternalApiException(EXTERNAL_API_EXCEPTION .withDetail(response.message()) .setStatus(HttpStatus.valueOf(response.resultCode()))); diff --git a/core/src/main/java/moa/cs/application/PersonalInquiryEventHandler.java b/core/src/main/java/moa/cs/application/PersonalInquiryEventHandler.java index 40864651..61e72dd7 100644 --- a/core/src/main/java/moa/cs/application/PersonalInquiryEventHandler.java +++ b/core/src/main/java/moa/cs/application/PersonalInquiryEventHandler.java @@ -1,19 +1,16 @@ package moa.cs.application; -import static moa.global.config.async.AsyncConfig.VIRTUAL_THREAD_EXECUTOR; import static org.springframework.transaction.event.TransactionPhase.AFTER_COMMIT; import lombok.RequiredArgsConstructor; import moa.client.discord.DiscordWebHookClient; import moa.cs.domain.PersonalInquiry; import moa.cs.domain.PersonalInquiryCreateEvent; -import org.springframework.scheduling.annotation.Async; import org.springframework.stereotype.Service; import org.springframework.transaction.event.TransactionalEventListener; @Service @RequiredArgsConstructor -@Async(VIRTUAL_THREAD_EXECUTOR) public class PersonalInquiryEventHandler { private static final String MESSAGE_FORMAT = """ @@ -28,7 +25,7 @@ public class PersonalInquiryEventHandler { @TransactionalEventListener(value = PersonalInquiryCreateEvent.class, phase = AFTER_COMMIT) public void push(PersonalInquiryCreateEvent event) { PersonalInquiry inquiry = event.inquiry(); - discordWebHookClient.sendContent( + discordWebHookClient.sendToInquiryChannel( MESSAGE_FORMAT .formatted( inquiry.getCategory(), diff --git a/core/src/main/resources/application-core-local.yml b/core/src/main/resources/application-core-local.yml index 69fd010f..ff156254 100644 --- a/core/src/main/resources/application-core-local.yml +++ b/core/src/main/resources/application-core-local.yml @@ -87,5 +87,9 @@ wincube: rsaPublicKey: "" discord: - webhookId: "id" - webhookToken: "token" + inquiry-channel: + webhookId: "id" + webhookToken: "token" + error-channel: + webhookId: "id" + webhookToken: "token"