Skip to content

Commit 5fced79

Browse files
Merge pull request #241 from prgrms-web-devcourse-final-project/feature/EA3-182-application-alarm
[EA3-182] feature: 스터디 신청 알림 기능 추가
2 parents 14a6443 + de8d5f4 commit 5fced79

File tree

9 files changed

+191
-2
lines changed

9 files changed

+191
-2
lines changed

src/main/java/grep/neogulcoder/domain/alram/service/AlarmService.java

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@
66
import grep.neogulcoder.domain.alram.repository.AlarmRepository;
77
import grep.neogulcoder.domain.alram.type.AlarmType;
88
import grep.neogulcoder.domain.alram.type.DomainType;
9+
import grep.neogulcoder.domain.recruitment.post.RecruitmentPost;
10+
import grep.neogulcoder.domain.recruitment.post.repository.RecruitmentPostRepository;
911
import grep.neogulcoder.domain.study.Study;
1012
import grep.neogulcoder.domain.study.StudyMember;
1113
import grep.neogulcoder.domain.study.enums.StudyMemberRole;
@@ -15,6 +17,10 @@
1517
import grep.neogulcoder.domain.study.repository.StudyMemberQueryRepository;
1618
import grep.neogulcoder.domain.study.repository.StudyMemberRepository;
1719
import grep.neogulcoder.domain.study.repository.StudyRepository;
20+
import grep.neogulcoder.domain.studyapplication.StudyApplication;
21+
import grep.neogulcoder.domain.studyapplication.event.ApplicationEvent;
22+
import grep.neogulcoder.domain.studyapplication.event.ApplicationStatusChangedEvent;
23+
import grep.neogulcoder.domain.studyapplication.repository.ApplicationRepository;
1824
import grep.neogulcoder.domain.timevote.event.TimeVotePeriodCreatedEvent;
1925
import grep.neogulcoder.global.exception.business.BusinessException;
2026
import grep.neogulcoder.global.exception.business.NotFoundException;
@@ -27,6 +33,7 @@
2733
import org.springframework.stereotype.Service;
2834
import org.springframework.transaction.annotation.Transactional;
2935

36+
import static grep.neogulcoder.domain.recruitment.RecruitmentErrorCode.NOT_FOUND;
3037
import static grep.neogulcoder.domain.study.exception.code.StudyErrorCode.*;
3138
import static grep.neogulcoder.domain.studyapplication.exception.code.ApplicationErrorCode.*;
3239

@@ -40,6 +47,8 @@ public class AlarmService {
4047
private final StudyRepository studyRepository;
4148
private final StudyMemberQueryRepository studyMemberQueryRepository;
4249
private final StudyMemberRepository studyMemberRepository;
50+
private final RecruitmentPostRepository recruitmentPostRepository;
51+
private final ApplicationRepository applicationRepository;
4352

4453
@Transactional
4554
public void saveAlarm(Long receiverId, AlarmType alarmType, DomainType domainType, Long domainId) {
@@ -154,6 +163,32 @@ public void handleTimeVotePeriodCreatedEvent(TimeVotePeriodCreatedEvent event) {
154163
}
155164
}
156165

166+
@EventListener
167+
public void handleApplicationEvent(ApplicationEvent event) {
168+
RecruitmentPost recruitmentPost = recruitmentPostRepository.findByIdAndActivatedTrue(event.recruitmentPostId())
169+
.orElseThrow(() -> new BusinessException(NOT_FOUND));
170+
171+
saveAlarm(
172+
recruitmentPost.getUserId(),
173+
AlarmType.STUDY_APPLICATION,
174+
DomainType.RECRUITMENT_POST,
175+
event.recruitmentPostId()
176+
);
177+
}
178+
179+
@EventListener
180+
public void handleApplicationStatusChangedEvent(ApplicationStatusChangedEvent event) {
181+
StudyApplication application = applicationRepository.findByIdAndActivatedTrue(event.applicationId())
182+
.orElseThrow(() -> new BusinessException(APPLICATION_NOT_FOUND));
183+
184+
saveAlarm(
185+
application.getUserId(),
186+
event.alarmType(),
187+
DomainType.STUDY_APPLICATION,
188+
application.getId()
189+
);
190+
}
191+
157192
private Alarm findValidAlarm(Long alarmId) {
158193
return alarmRepository.findById(alarmId).orElseThrow(() -> new NotFoundException(AlarmErrorCode.ALARM_NOT_FOUND));
159194
}
Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,11 @@
11
package grep.neogulcoder.domain.alram.type;
22

33
public enum AlarmType {
4-
INVITE, STUDY_EXTEND, STUDY_EXTENSION_REMINDER, TIME_VOTE_REQUEST
4+
INVITE,
5+
STUDY_EXTEND,
6+
STUDY_EXTENSION_REMINDER,
7+
TIME_VOTE_REQUEST,
8+
STUDY_APPLICATION,
9+
STUDY_APPLICATION_APPROVED,
10+
STUDY_APPLICATION_REJECTED
511
}
Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,8 @@
11
package grep.neogulcoder.domain.alram.type;
22

33
public enum DomainType {
4-
STUDY, TIME_VOTE
4+
STUDY,
5+
TIME_VOTE,
6+
RECRUITMENT_POST,
7+
STUDY_APPLICATION
58
}
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
package grep.neogulcoder.domain.studyapplication.event;
2+
3+
public record ApplicationEvent(Long recruitmentPostId, Long applicationId) {
4+
}
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
package grep.neogulcoder.domain.studyapplication.event;
2+
3+
import grep.neogulcoder.domain.alram.type.AlarmType;
4+
5+
public record ApplicationStatusChangedEvent(Long applicationId, AlarmType alarmType) {
6+
}
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
package grep.neogulcoder.domain.studyapplication.provider;
2+
3+
import grep.neogulcoder.domain.alram.type.*;
4+
import grep.neogulcoder.domain.recruitment.post.RecruitmentPost;
5+
import grep.neogulcoder.domain.recruitment.post.repository.RecruitmentPostRepository;
6+
import grep.neogulcoder.domain.studyapplication.StudyApplication;
7+
import grep.neogulcoder.domain.studyapplication.repository.ApplicationRepository;
8+
import grep.neogulcoder.global.exception.business.NotFoundException;
9+
import grep.neogulcoder.global.provider.MessageProvidable;
10+
import lombok.RequiredArgsConstructor;
11+
import org.springframework.stereotype.Component;
12+
13+
import static grep.neogulcoder.domain.recruitment.RecruitmentErrorCode.*;
14+
import static grep.neogulcoder.domain.studyapplication.exception.code.ApplicationErrorCode.*;
15+
16+
@Component
17+
@RequiredArgsConstructor
18+
public class ApplicationApprovedMessageProvider implements MessageProvidable {
19+
20+
private final ApplicationRepository applicationRepository;
21+
private final RecruitmentPostRepository recruitmentPostRepository;
22+
23+
@Override
24+
public boolean isSupport(AlarmType alarmType) {
25+
return alarmType == AlarmType.STUDY_APPLICATION_APPROVED;
26+
}
27+
28+
@Override
29+
public String provideMessage(DomainType domainType, Long domainId) {
30+
if (domainType != DomainType.STUDY_APPLICATION) {
31+
throw new IllegalArgumentException("스터디 신청 승인 알림은 STUDY_APPLICATION 도메인에만 해당됩니다.");
32+
}
33+
34+
StudyApplication application = applicationRepository.findByIdAndActivatedTrue(domainId)
35+
.orElseThrow(() -> new NotFoundException(APPLICATION_NOT_FOUND));
36+
37+
String subject = recruitmentPostRepository.findById(application.getRecruitmentPostId())
38+
.map(RecruitmentPost::getSubject)
39+
.orElseThrow(() -> new NotFoundException(NOT_FOUND));
40+
41+
return String.format("당신이 지원한 '%s' 스터디가 승인되었습니다.", subject);
42+
}
43+
}
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
package grep.neogulcoder.domain.studyapplication.provider;
2+
3+
import grep.neogulcoder.domain.alram.type.AlarmType;
4+
import grep.neogulcoder.domain.alram.type.DomainType;
5+
import grep.neogulcoder.domain.recruitment.post.RecruitmentPost;
6+
import grep.neogulcoder.domain.recruitment.post.repository.RecruitmentPostRepository;
7+
import grep.neogulcoder.global.exception.business.NotFoundException;
8+
import grep.neogulcoder.global.provider.MessageProvidable;
9+
import lombok.RequiredArgsConstructor;
10+
import org.springframework.stereotype.Component;
11+
12+
import static grep.neogulcoder.domain.recruitment.RecruitmentErrorCode.*;
13+
14+
@Component
15+
@RequiredArgsConstructor
16+
public class ApplicationMessageProvider implements MessageProvidable {
17+
18+
private final RecruitmentPostRepository recruitmentPostRepository;
19+
20+
@Override
21+
public boolean isSupport(AlarmType alarmType) {
22+
return alarmType == AlarmType.STUDY_APPLICATION;
23+
}
24+
25+
@Override
26+
public String provideMessage(DomainType domainType, Long domainId) {
27+
if (domainType != DomainType.RECRUITMENT_POST) {
28+
throw new IllegalArgumentException("스터디 신청 알림은 RECRUITMENT_POST 도메인에만 해당됩니다.");
29+
}
30+
31+
String recruitmentPostSubject = recruitmentPostRepository.findByIdAndActivatedTrue(domainId)
32+
.map(RecruitmentPost::getSubject)
33+
.orElseThrow(() -> new NotFoundException(NOT_FOUND));
34+
35+
return String.format("모집글 '%s'에 새로운 신청이 들어왔습니다.", recruitmentPostSubject);
36+
}
37+
}
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
package grep.neogulcoder.domain.studyapplication.provider;
2+
3+
import grep.neogulcoder.domain.alram.type.AlarmType;
4+
import grep.neogulcoder.domain.alram.type.DomainType;
5+
import grep.neogulcoder.domain.recruitment.post.RecruitmentPost;
6+
import grep.neogulcoder.domain.recruitment.post.repository.RecruitmentPostRepository;
7+
import grep.neogulcoder.domain.studyapplication.StudyApplication;
8+
import grep.neogulcoder.domain.studyapplication.repository.ApplicationRepository;
9+
import grep.neogulcoder.global.exception.business.NotFoundException;
10+
import grep.neogulcoder.global.provider.MessageProvidable;
11+
import lombok.RequiredArgsConstructor;
12+
import org.springframework.stereotype.Component;
13+
14+
import static grep.neogulcoder.domain.recruitment.RecruitmentErrorCode.*;
15+
import static grep.neogulcoder.domain.studyapplication.exception.code.ApplicationErrorCode.*;
16+
17+
@Component
18+
@RequiredArgsConstructor
19+
public class ApplicationRejectedMessageProvider implements MessageProvidable {
20+
21+
private final ApplicationRepository applicationRepository;
22+
private final RecruitmentPostRepository recruitmentPostRepository;
23+
24+
@Override
25+
public boolean isSupport(AlarmType alarmType) {
26+
return alarmType == AlarmType.STUDY_APPLICATION_REJECTED;
27+
}
28+
29+
@Override
30+
public String provideMessage(DomainType domainType, Long domainId) {
31+
if (domainType != DomainType.STUDY_APPLICATION) {
32+
throw new IllegalArgumentException("스터디 신청 거절 알림은 STUDY_APPLICATION 도메인에만 해당됩니다.");
33+
}
34+
35+
StudyApplication application = applicationRepository.findByIdAndActivatedTrue(domainId)
36+
.orElseThrow(() -> new NotFoundException(APPLICATION_NOT_FOUND));
37+
38+
String subject = recruitmentPostRepository.findById(application.getRecruitmentPostId())
39+
.map(RecruitmentPost::getSubject)
40+
.orElseThrow(() -> new NotFoundException(NOT_FOUND));
41+
42+
return String.format("당신이 지원한 '%s' 스터디가 거절되었습니다.", subject);
43+
}
44+
}

src/main/java/grep/neogulcoder/domain/studyapplication/service/ApplicationService.java

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
package grep.neogulcoder.domain.studyapplication.service;
22

3+
import grep.neogulcoder.domain.alram.type.AlarmType;
34
import grep.neogulcoder.domain.recruitment.post.RecruitmentPost;
45
import grep.neogulcoder.domain.recruitment.post.repository.RecruitmentPostRepository;
56
import grep.neogulcoder.domain.study.Study;
@@ -15,11 +16,14 @@
1516
import grep.neogulcoder.domain.studyapplication.controller.dto.response.MyApplicationResponse;
1617
import grep.neogulcoder.domain.studyapplication.controller.dto.response.ReceivedApplicationPagingResponse;
1718
import grep.neogulcoder.domain.studyapplication.controller.dto.response.ReceivedApplicationResponse;
19+
import grep.neogulcoder.domain.studyapplication.event.ApplicationEvent;
20+
import grep.neogulcoder.domain.studyapplication.event.ApplicationStatusChangedEvent;
1821
import grep.neogulcoder.domain.studyapplication.repository.ApplicationQueryRepository;
1922
import grep.neogulcoder.domain.studyapplication.repository.ApplicationRepository;
2023
import grep.neogulcoder.global.exception.business.BusinessException;
2124
import grep.neogulcoder.global.exception.business.NotFoundException;
2225
import lombok.RequiredArgsConstructor;
26+
import org.springframework.context.ApplicationEventPublisher;
2327
import org.springframework.data.domain.Page;
2428
import org.springframework.data.domain.Pageable;
2529
import org.springframework.stereotype.Service;
@@ -40,6 +44,7 @@ public class ApplicationService {
4044
private final StudyMemberRepository studyMemberRepository;
4145
private final StudyRepository studyRepository;
4246
private final StudyMemberQueryRepository studyMemberQueryRepository;
47+
private final ApplicationEventPublisher eventPublisher;
4348

4449
@Transactional
4550
public ReceivedApplicationPagingResponse getReceivedApplicationsPaging(Long recruitmentPostId, Pageable pageable, Long userId) {
@@ -68,6 +73,8 @@ public Long createApplication(Long recruitmentPostId, ApplicationCreateRequest r
6873
StudyApplication application = request.toEntity(recruitmentPostId, userId);
6974
applicationRepository.save(application);
7075

76+
eventPublisher.publishEvent(new ApplicationEvent(recruitmentPostId, application.getId()));
77+
7178
return application.getId();
7279
}
7380

@@ -86,6 +93,8 @@ public void approveApplication(Long applicationId, Long userId) {
8693
StudyMember studyMember = StudyMember.createMember(study, application.getUserId());
8794
studyMemberRepository.save(studyMember);
8895
study.increaseMemberCount();
96+
97+
eventPublisher.publishEvent(new ApplicationStatusChangedEvent(applicationId, AlarmType.STUDY_APPLICATION_APPROVED));
8998
}
9099

91100
@Transactional
@@ -98,6 +107,8 @@ public void rejectApplication(Long applicationId, Long userId) {
98107
validateStatusIsApplying(application);
99108

100109
application.reject();
110+
111+
eventPublisher.publishEvent(new ApplicationStatusChangedEvent(applicationId, AlarmType.STUDY_APPLICATION_REJECTED));
101112
}
102113

103114
private Study findValidStudy(RecruitmentPost post) {

0 commit comments

Comments
 (0)