Skip to content

Commit e0ebcb1

Browse files
authored
feat: 어드민 인증 필요 시 알림을 보내도록 (#828)
* feat: Discord 검수 알림 전송을 위한 DiscordNotifier 추가 * feat: 학점/어학 성적 등록 완료 시 Discord 검수 알림 전송 * feat: 멘토 신청 등록 완료 시 Discord 검수 알림 전송 * feat: 신고 등록 완료 시 Discord 검수 알림 전송 * fix: 환경 미지정 시 기본값을 local 대신 빈 문자열로 변경 * refactor: notifyReviewRequested를 notify로 이름 변경
1 parent 2a8ec34 commit e0ebcb1

5 files changed

Lines changed: 82 additions & 0 deletions

File tree

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
package com.example.solidconnection.common.discord;
2+
3+
import lombok.Getter;
4+
5+
@Getter
6+
public enum DiscordNotificationType {
7+
8+
GPA_SCORE("학점 성적"),
9+
LANGUAGE_TEST_SCORE("어학 성적"),
10+
MENTOR_APPLICATION("멘토 신청"),
11+
REPORT("신고"),
12+
;
13+
14+
private final String displayName;
15+
16+
DiscordNotificationType(String displayName) {
17+
this.displayName = displayName;
18+
}
19+
}
Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
package com.example.solidconnection.common.discord;
2+
3+
import java.util.Map;
4+
import lombok.RequiredArgsConstructor;
5+
import lombok.extern.slf4j.Slf4j;
6+
import org.springframework.beans.factory.annotation.Value;
7+
import org.springframework.http.HttpEntity;
8+
import org.springframework.http.HttpHeaders;
9+
import org.springframework.http.MediaType;
10+
import org.springframework.scheduling.annotation.Async;
11+
import org.springframework.scheduling.annotation.EnableAsync;
12+
import org.springframework.stereotype.Component;
13+
import org.springframework.web.client.RestTemplate;
14+
15+
@Component
16+
@RequiredArgsConstructor
17+
@EnableAsync
18+
@Slf4j
19+
public class DiscordNotifier {
20+
21+
private static final String ADMIN_PAGE_URL = "https://admins.solid-connection.com";
22+
23+
private final RestTemplate restTemplate;
24+
25+
@Value("${discord.webhook-url:}")
26+
private String webhookUrl;
27+
28+
@Value("${spring.profiles.active:}")
29+
private String environment;
30+
31+
@Async
32+
public void notify(DiscordNotificationType type, String applicantInfo) {
33+
if (webhookUrl.isBlank()) {
34+
return;
35+
}
36+
try {
37+
HttpHeaders headers = new HttpHeaders();
38+
headers.setContentType(MediaType.APPLICATION_JSON);
39+
HttpEntity<Map<String, String>> request = new HttpEntity<>(Map.of("content", buildMessage(type, applicantInfo)), headers);
40+
restTemplate.postForEntity(webhookUrl, request, Void.class);
41+
} catch (Exception e) {
42+
log.error("Discord 검수 알림 전송 실패. type={}, applicantInfo={}", type, applicantInfo, e);
43+
}
44+
}
45+
46+
private String buildMessage(DiscordNotificationType type, String applicantInfo) {
47+
return "[%s] %s 검수 요청이 등록되었습니다.\n신청자: %s\n관리자 페이지: %s"
48+
.formatted(environment.toUpperCase(), type.getDisplayName(), applicantInfo, ADMIN_PAGE_URL);
49+
}
50+
}

‎src/main/java/com/example/solidconnection/mentor/service/MentorApplicationService.java‎

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@
55
import static com.example.solidconnection.common.exception.ErrorCode.TERM_NOT_FOUND;
66
import static com.example.solidconnection.common.exception.ErrorCode.USER_NOT_FOUND;
77

8+
import com.example.solidconnection.common.discord.DiscordNotificationType;
9+
import com.example.solidconnection.common.discord.DiscordNotifier;
810
import com.example.solidconnection.common.exception.CustomException;
911
import com.example.solidconnection.mentor.domain.MentorApplication;
1012
import com.example.solidconnection.mentor.domain.MentorApplicationStatus;
@@ -35,6 +37,7 @@ public class MentorApplicationService {
3537
private final SiteUserRepository siteUserRepository;
3638
private final S3Service s3Service;
3739
private final TermRepository termRepository;
40+
private final DiscordNotifier discordNotifier;
3841

3942
@Transactional
4043
public void submitMentorApplication(
@@ -60,6 +63,7 @@ public void submitMentorApplication(
6063
mentorApplicationRequest.exchangeStatus()
6164
);
6265
mentorApplicationRepository.save(mentorApplication);
66+
discordNotifier.notify(DiscordNotificationType.MENTOR_APPLICATION, siteUser.getNickname());
6367
}
6468

6569
private void ensureNoPendingOrApprovedMentorApplication(long siteUserId) {

‎src/main/java/com/example/solidconnection/report/service/ReportService.java‎

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@
44
import com.example.solidconnection.chat.domain.ChatParticipant;
55
import com.example.solidconnection.chat.repository.ChatMessageRepository;
66
import com.example.solidconnection.chat.repository.ChatParticipantRepository;
7+
import com.example.solidconnection.common.discord.DiscordNotificationType;
8+
import com.example.solidconnection.common.discord.DiscordNotifier;
79
import com.example.solidconnection.common.exception.CustomException;
810
import com.example.solidconnection.common.exception.ErrorCode;
911
import com.example.solidconnection.community.post.domain.Post;
@@ -28,6 +30,7 @@ public class ReportService {
2830
private final PostRepository postRepository;
2931
private final ChatMessageRepository chatMessageRepository;
3032
private final ChatParticipantRepository chatParticipantRepository;
33+
private final DiscordNotifier discordNotifier;
3134

3235
@Transactional
3336
public void createReport(long reporterId, ReportRequest request) {
@@ -39,6 +42,7 @@ public void createReport(long reporterId, ReportRequest request) {
3942

4043
Report report = new Report(reporterId, reportedId, request.reportType(), request.targetType(), request.targetId());
4144
reportRepository.save(report);
45+
discordNotifier.notify(DiscordNotificationType.REPORT, "신고자 ID: " + reporterId);
4246
}
4347

4448
private void validateReporterAndReportedExists(long reporterId, long reportedId) {

‎src/main/java/com/example/solidconnection/score/service/ScoreService.java‎

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@
44

55
import com.example.solidconnection.application.domain.Gpa;
66
import com.example.solidconnection.application.domain.LanguageTest;
7+
import com.example.solidconnection.common.discord.DiscordNotificationType;
8+
import com.example.solidconnection.common.discord.DiscordNotifier;
79
import com.example.solidconnection.common.exception.CustomException;
810
import com.example.solidconnection.s3.domain.UploadPath;
911
import com.example.solidconnection.s3.dto.UploadedFileUrlResponse;
@@ -37,6 +39,7 @@ public class ScoreService {
3739
private final LanguageTestScoreRepository languageTestScoreRepository;
3840
private final SiteUserRepository siteUserRepository;
3941
private final HomeUniversityQueryService homeUniversityQueryService;
42+
private final DiscordNotifier discordNotifier;
4043

4144
@Transactional
4245
public Long submitGpaScore(long siteUserId, GpaScoreRequest gpaScoreRequest, MultipartFile file) {
@@ -46,6 +49,7 @@ public Long submitGpaScore(long siteUserId, GpaScoreRequest gpaScoreRequest, Mul
4649
Gpa gpa = new Gpa(gpaScoreRequest.gpa(), gpaScoreRequest.gpaCriteria(), uploadedFile.fileUrl());
4750
GpaScore newGpaScore = new GpaScore(gpa, siteUser);
4851
GpaScore savedNewGpaScore = gpaScoreRepository.save(newGpaScore);
52+
discordNotifier.notify(DiscordNotificationType.GPA_SCORE, siteUser.getNickname());
4953
return savedNewGpaScore.getId();
5054
}
5155

@@ -58,6 +62,7 @@ public Long submitLanguageTestScore(long siteUserId, LanguageTestScoreRequest la
5862
languageTestScoreRequest.languageTestScore(), uploadedFile.fileUrl());
5963
LanguageTestScore newScore = new LanguageTestScore(languageTest, siteUser);
6064
LanguageTestScore savedNewScore = languageTestScoreRepository.save(newScore);
65+
discordNotifier.notify(DiscordNotificationType.LANGUAGE_TEST_SCORE, siteUser.getNickname());
6166
return savedNewScore.getId();
6267
}
6368

0 commit comments

Comments
 (0)