From 8cb6d1a58a8e7cdc9dbd114c4bfcf427be16d869 Mon Sep 17 00:00:00 2001 From: minsu20 Date: Sat, 18 Nov 2023 17:50:37 +0900 Subject: [PATCH 1/3] =?UTF-8?q?fix:=20email=20nullable=20true=EB=A1=9C=20?= =?UTF-8?q?=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../com/moing/backend/domain/member/domain/entity/Member.java | 1 - 1 file changed, 1 deletion(-) diff --git a/src/main/java/com/moing/backend/domain/member/domain/entity/Member.java b/src/main/java/com/moing/backend/domain/member/domain/entity/Member.java index ca87d769..2c8b2310 100644 --- a/src/main/java/com/moing/backend/domain/member/domain/entity/Member.java +++ b/src/main/java/com/moing/backend/domain/member/domain/entity/Member.java @@ -45,7 +45,6 @@ public class Member extends BaseTimeEntity { private RegistrationStatus registrationStatus; @Convert(converter = AesConverter.class) - @Column(nullable = false) private String email; private String profileImage; From 4f74feb9a9b31e71dfc40c316b0c6db5359eec7a Mon Sep 17 00:00:00 2001 From: minsu20 Date: Mon, 20 Nov 2023 22:01:11 +0900 Subject: [PATCH 2/3] =?UTF-8?q?feat:=20=EC=97=90=EB=9F=AC=20=EB=B0=9C?= =?UTF-8?q?=EC=83=9D=20=EC=8B=9C=20slack=20Alarm=EB=B3=B4=EB=82=B4?= =?UTF-8?q?=EB=8A=94=20=EA=B8=B0=EB=8A=A5=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- build.gradle | 2 + .../exception/GlobalExceptionHandler.java | 8 ++- .../backend/global/util/SlackService.java | 65 +++++++++++++++++++ 3 files changed, 74 insertions(+), 1 deletion(-) create mode 100644 src/main/java/com/moing/backend/global/util/SlackService.java diff --git a/build.gradle b/build.gradle index 2fd7bb34..74cdd0b3 100644 --- a/build.gradle +++ b/build.gradle @@ -91,6 +91,8 @@ dependencies { implementation platform('software.amazon.awssdk:bom:2.17.230') implementation 'software.amazon.awssdk:s3' + //slack + implementation 'com.slack.api:slack-api-client:1.30.0' } diff --git a/src/main/java/com/moing/backend/global/exception/GlobalExceptionHandler.java b/src/main/java/com/moing/backend/global/exception/GlobalExceptionHandler.java index 7a5b3529..04298e10 100644 --- a/src/main/java/com/moing/backend/global/exception/GlobalExceptionHandler.java +++ b/src/main/java/com/moing/backend/global/exception/GlobalExceptionHandler.java @@ -2,6 +2,8 @@ import com.moing.backend.global.response.ErrorCode; import com.moing.backend.global.response.ErrorResponse; +import com.moing.backend.global.util.SlackService; +import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; @@ -12,15 +14,18 @@ import org.springframework.web.bind.annotation.RestControllerAdvice; import org.springframework.context.support.DefaultMessageSourceResolvable; +import javax.servlet.http.HttpServletRequest; import java.util.function.Consumer; import java.util.regex.PatternSyntaxException; import java.util.stream.Collectors; @RestControllerAdvice @Slf4j +@RequiredArgsConstructor public class GlobalExceptionHandler { private static final String LOG_FORMAT = "Class : {}, Code : {}, Message : {}"; + private final SlackService slackService; @ExceptionHandler(ApplicationException.class) public ResponseEntity handleApplicationException(ApplicationException ex) { @@ -51,7 +56,8 @@ public ResponseEntity httpRequestNotSupportedExceptionHandler(Htt } @ExceptionHandler(Exception.class) - public ResponseEntity internalServerErrorHandler(Exception ex) { + public ResponseEntity internalServerErrorHandler(Exception ex, HttpServletRequest request) { + slackService.sendSlackAlertErrorLog(ex, request); return handleException(ex, ErrorCode.INTERNAL_SERVER_ERROR, ErrorCode.INTERNAL_SERVER_ERROR.getMessage(), HttpStatus.INTERNAL_SERVER_ERROR, log::error); } diff --git a/src/main/java/com/moing/backend/global/util/SlackService.java b/src/main/java/com/moing/backend/global/util/SlackService.java new file mode 100644 index 00000000..97a14dd4 --- /dev/null +++ b/src/main/java/com/moing/backend/global/util/SlackService.java @@ -0,0 +1,65 @@ +package com.moing.backend.global.util; + +import com.slack.api.Slack; +import com.slack.api.model.Attachment; +import com.slack.api.model.Field; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.stereotype.Service; + +import javax.servlet.http.HttpServletRequest; +import java.io.IOException; +import java.time.LocalDateTime; +import java.time.format.DateTimeFormatter; +import java.util.List; + +import static com.slack.api.webhook.WebhookPayloads.payload; + +@Service +@Slf4j +public class SlackService { + + @Value("${webhook.slack.error_url}") + private String errorWebhookUrl; + + private final Slack slackClient = Slack.getInstance(); + + // 슬랙 에러 알림 보내는 메서드 + public void sendSlackAlertErrorLog(Exception e, HttpServletRequest request) { + try { + slackClient.send(errorWebhookUrl, payload(p -> p + .text("[500 에러가 발생했습니다.]") + .attachments( + List.of(generateSlackAttachment(e, request)) + ) + )); + } catch (IOException slackError) { + log.debug("Slack 통신과의 예외 발생"); + } + } + + // attachment 생성 메서드 + private Attachment generateSlackAttachment(Exception e, HttpServletRequest request) { + String requestTime = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss.SSS").format(LocalDateTime.now()); + String xffHeader = request.getHeader("X-FORWARDED-FOR"); + return Attachment.builder() + .color("ff0000") + .title(requestTime + " 발생 에러 로그") + .fields(List.of( + generateSlackField("Request IP", xffHeader == null ? request.getRemoteAddr() : xffHeader), + generateSlackField("Request URL", request.getRequestURL() + " " + request.getMethod()), + generateSlackField("Error Message", e.getMessage()) + ) + ) + .build(); + } + + private Field generateSlackField(String title, String value) { + return Field.builder() + .title(title) + .value(value) + .valueShortEnough(false) + .build(); + } + +} From ae99b7b7c24ce08c206327e8ec8178b84f251f5c Mon Sep 17 00:00:00 2001 From: minsu20 Date: Mon, 20 Nov 2023 22:15:45 +0900 Subject: [PATCH 3/3] =?UTF-8?q?feat:=20=EC=86=8C=EB=AA=A8=EC=9E=84=20?= =?UTF-8?q?=EC=83=9D=EC=84=B1=20=EC=8B=9C=20slack=20Alarm=20=EB=B3=B4?= =?UTF-8?q?=EB=82=B4=EB=8A=94=20=EA=B8=B0=EB=8A=A5=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../service/CreateTeamUserCase.java | 3 ++ .../backend/global/util/SlackService.java | 44 +++++++++++++++---- 2 files changed, 39 insertions(+), 8 deletions(-) diff --git a/src/main/java/com/moing/backend/domain/team/application/service/CreateTeamUserCase.java b/src/main/java/com/moing/backend/domain/team/application/service/CreateTeamUserCase.java index 262e7a8b..1b6238a1 100644 --- a/src/main/java/com/moing/backend/domain/team/application/service/CreateTeamUserCase.java +++ b/src/main/java/com/moing/backend/domain/team/application/service/CreateTeamUserCase.java @@ -10,6 +10,7 @@ import com.moing.backend.domain.teamMember.domain.service.TeamMemberSaveService; import com.moing.backend.domain.teamScore.application.mapper.TeamScoreMapper; import com.moing.backend.domain.teamScore.domain.service.TeamScoreSaveService; +import com.moing.backend.global.util.SlackService; import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Service; @@ -26,6 +27,7 @@ public class CreateTeamUserCase { private final TeamMapper teamMapper; private final TeamScoreSaveService teamScoreSaveService; private final TeamScoreMapper teamScoreMapper; + private final SlackService slackService; public CreateTeamResponse createTeam(CreateTeamRequest createTeamRequest, String socialId){ Member member = memberGetService.getMemberBySocialId(socialId); @@ -36,6 +38,7 @@ public CreateTeamResponse createTeam(CreateTeamRequest createTeamRequest, String team.approveTeam(); //====지워야 함 (테스트 용)===== teamScoreSaveService.save(teamScoreMapper.mapToTeamScore(team)); // 팀스코어 엔티티 생성 + slackService.sendSlackTeamCreatedMessage(team.getName(), team.getLeaderId()); return new CreateTeamResponse(team.getTeamId()); } } diff --git a/src/main/java/com/moing/backend/global/util/SlackService.java b/src/main/java/com/moing/backend/global/util/SlackService.java index 97a14dd4..813a52b7 100644 --- a/src/main/java/com/moing/backend/global/util/SlackService.java +++ b/src/main/java/com/moing/backend/global/util/SlackService.java @@ -5,6 +5,7 @@ import com.slack.api.model.Field; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Value; +import org.springframework.scheduling.annotation.Async; import org.springframework.stereotype.Service; import javax.servlet.http.HttpServletRequest; @@ -22,24 +23,51 @@ public class SlackService { @Value("${webhook.slack.error_url}") private String errorWebhookUrl; + @Value("${webhook.slack.team_alarm_url}") + private String teamAlarmWebhookUrl; + private final Slack slackClient = Slack.getInstance(); - // 슬랙 에러 알림 보내는 메서드 - public void sendSlackAlertErrorLog(Exception e, HttpServletRequest request) { + // 공통 슬랙 메시지 전송 메서드 + @Async + public void sendSlackMessage(String webhookUrl, String message, List attachments) { try { - slackClient.send(errorWebhookUrl, payload(p -> p - .text("[500 에러가 발생했습니다.]") - .attachments( - List.of(generateSlackAttachment(e, request)) - ) + slackClient.send(webhookUrl, payload(p -> p + .text(message) + .attachments(attachments) )); } catch (IOException slackError) { log.debug("Slack 통신과의 예외 발생"); } } + // 슬랙 에러 알림 메서드 + public void sendSlackAlertErrorLog(Exception e, HttpServletRequest request) { + String message = "[500 에러가 발생했습니다.]"; + List attachments = List.of(generateSlackErrorAttachment(e, request)); + sendSlackMessage(errorWebhookUrl, message, attachments); + } + + // 슬랙 소모임 생성 알림 메서드 + public void sendSlackTeamCreatedMessage(String teamName, Long leaderId) { + String message = String.format("새로운 소모임 '%s'이(가) 생성되었습니다!", teamName); + List attachments = List.of(generateSlackTeamAttachment(teamName, leaderId)); + sendSlackMessage(teamAlarmWebhookUrl, message, attachments); + } + + private Attachment generateSlackTeamAttachment(String teamName, Long leaderId) { + return Attachment.builder() + .color("36a64f") + .title("소모임 생성 알림") + .fields(List.of( + generateSlackField("소모임 이름", teamName), + generateSlackField("생성자 아이디", String.valueOf(leaderId)) + )) + .build(); + } + // attachment 생성 메서드 - private Attachment generateSlackAttachment(Exception e, HttpServletRequest request) { + private Attachment generateSlackErrorAttachment(Exception e, HttpServletRequest request) { String requestTime = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss.SSS").format(LocalDateTime.now()); String xffHeader = request.getHeader("X-FORWARDED-FOR"); return Attachment.builder()