Skip to content

Commit 379ed02

Browse files
Merge pull request #246 from prgrms-web-devcourse-final-project/develop
[EA3-68] chore: develop → main 브랜치 머지
2 parents c1f19e0 + 31be01f commit 379ed02

File tree

16 files changed

+314
-72
lines changed

16 files changed

+314
-72
lines changed

src/main/java/grep/neogulcoder/domain/alram/controller/dto/response/AlarmResponse.java

Lines changed: 17 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
import grep.neogulcoder.domain.alram.type.AlarmType;
44
import grep.neogulcoder.domain.alram.type.DomainType;
5+
import java.time.LocalDateTime;
56
import lombok.Builder;
67
import lombok.Data;
78

@@ -22,28 +23,33 @@ public class AlarmResponse {
2223

2324
private boolean checked;
2425

25-
public static AlarmResponse toResponse(Long id, Long receiverUserId, AlarmType alarmType, DomainType domainType,
26-
Long domainId, String message, boolean checked) {
26+
private LocalDateTime createdDate;
27+
28+
public static AlarmResponse toResponse(Long id, Long receiverUserId, AlarmType alarmType,
29+
DomainType domainType,
30+
Long domainId, String message, boolean checked, LocalDateTime createdDate) {
2731
return AlarmResponse.builder()
28-
.id(id)
29-
.receiverUserId(receiverUserId)
30-
.alarmType(alarmType)
31-
.domainType(domainType)
32-
.domainId(domainId)
33-
.message(message)
34-
.checked(checked)
35-
.build();
32+
.id(id)
33+
.receiverUserId(receiverUserId)
34+
.alarmType(alarmType)
35+
.domainType(domainType)
36+
.domainId(domainId)
37+
.message(message)
38+
.checked(checked)
39+
.createdDate(createdDate)
40+
.build();
3641
}
3742

3843
@Builder
3944
private AlarmResponse(Long id, Long receiverUserId, AlarmType alarmType, DomainType domainType,
40-
Long domainId, String message, boolean checked) {
45+
Long domainId, String message, boolean checked, LocalDateTime createdDate) {
4146
this.id = id;
4247
this.receiverUserId = receiverUserId;
4348
this.alarmType = alarmType;
4449
this.domainType = domainType;
4550
this.domainId = domainId;
4651
this.message = message;
4752
this.checked = checked;
53+
this.createdDate = createdDate;
4854
}
4955
}

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

Lines changed: 81 additions & 41 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,18 +17,21 @@
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;
2127
import grep.neogulcoder.global.provider.finder.MessageFinder;
22-
2328
import java.util.List;
24-
2529
import lombok.RequiredArgsConstructor;
2630
import org.springframework.context.event.EventListener;
2731
import org.springframework.stereotype.Service;
2832
import org.springframework.transaction.annotation.Transactional;
2933

34+
import static grep.neogulcoder.domain.recruitment.RecruitmentErrorCode.NOT_FOUND;
3035
import static grep.neogulcoder.domain.study.exception.code.StudyErrorCode.*;
3136
import static grep.neogulcoder.domain.studyapplication.exception.code.ApplicationErrorCode.*;
3237

@@ -40,54 +45,59 @@ public class AlarmService {
4045
private final StudyRepository studyRepository;
4146
private final StudyMemberQueryRepository studyMemberQueryRepository;
4247
private final StudyMemberRepository studyMemberRepository;
48+
private final RecruitmentPostRepository recruitmentPostRepository;
49+
private final ApplicationRepository applicationRepository;
4350

4451
@Transactional
45-
public void saveAlarm(Long receiverId, AlarmType alarmType, DomainType domainType, Long domainId) {
52+
public void saveAlarm(Long receiverId, AlarmType alarmType, DomainType domainType,
53+
Long domainId) {
4654
String message = messageFinder.findMessage(alarmType, domainType, domainId);
4755
alarmRepository.save(Alarm.init(alarmType, receiverId, domainType, domainId, message));
4856
}
4957

5058
public List<AlarmResponse> getAllUncheckedAlarms(Long receiverUserId) {
5159
return alarmRepository.findAllByReceiverUserIdAndCheckedFalse(receiverUserId).stream()
52-
.map(alarm -> AlarmResponse.toResponse(
53-
alarm.getId(),
54-
alarm.getReceiverUserId(),
55-
alarm.getAlarmType(),
56-
alarm.getDomainType(),
57-
alarm.getDomainId(),
58-
alarm.getMessage(),
59-
alarm.isChecked()))
60-
.toList();
60+
.map(alarm -> AlarmResponse.toResponse(
61+
alarm.getId(),
62+
alarm.getReceiverUserId(),
63+
alarm.getAlarmType(),
64+
alarm.getDomainType(),
65+
alarm.getDomainId(),
66+
alarm.getMessage(),
67+
alarm.isChecked(),
68+
alarm.getCreatedDate()))
69+
.toList();
6170
}
6271

6372
public List<AlarmResponse> getAllAlarms(Long receiverUserId) {
6473
return alarmRepository.findAllByReceiverUserId(receiverUserId).stream()
65-
.map(alarm -> AlarmResponse.toResponse(
66-
alarm.getId(),
67-
alarm.getReceiverUserId(),
68-
alarm.getAlarmType(),
69-
alarm.getDomainType(),
70-
alarm.getDomainId(),
71-
alarm.getMessage(),
72-
alarm.isChecked()))
73-
.toList();
74+
.map(alarm -> AlarmResponse.toResponse(
75+
alarm.getId(),
76+
alarm.getReceiverUserId(),
77+
alarm.getAlarmType(),
78+
alarm.getDomainType(),
79+
alarm.getDomainId(),
80+
alarm.getMessage(),
81+
alarm.isChecked(),
82+
alarm.getCreatedDate()))
83+
.toList();
7484
}
7585

7686
@Transactional
7787
public void checkAllAlarmWithoutInvite(Long receiverUserId) {
7888
List<Alarm> alarms = alarmRepository.findAllByReceiverUserIdAndCheckedFalse(receiverUserId);
7989
alarms.stream()
80-
.filter(alarm -> alarm.getAlarmType() != AlarmType.INVITE)
81-
.forEach(Alarm::checkAlarm);
90+
.filter(alarm -> alarm.getAlarmType() != AlarmType.INVITE)
91+
.forEach(Alarm::checkAlarm);
8292
}
8393

8494
@EventListener
8595
public void handleStudyInviteEvent(StudyInviteEvent event) {
8696
saveAlarm(
87-
event.targetUserId(),
88-
AlarmType.INVITE,
89-
DomainType.STUDY,
90-
event.studyId()
97+
event.targetUserId(),
98+
AlarmType.INVITE,
99+
DomainType.STUDY,
100+
event.studyId()
91101
);
92102
}
93103

@@ -111,36 +121,39 @@ public void rejectInvite(Long alarmId) {
111121

112122
@EventListener
113123
public void handleStudyExtendEvent(StudyExtendEvent event) {
114-
List<StudyMember> members = studyMemberRepository.findAllByStudyIdAndActivatedTrue(event.studyId());
124+
List<StudyMember> members = studyMemberRepository.findAllByStudyIdAndActivatedTrue(
125+
event.studyId());
115126

116127
for (StudyMember member : members) {
117128
if (!member.isLeader()) {
118129
saveAlarm(
119-
member.getUserId(),
120-
AlarmType.STUDY_EXTEND,
121-
DomainType.STUDY,
122-
event.studyId()
130+
member.getUserId(),
131+
AlarmType.STUDY_EXTEND,
132+
DomainType.STUDY,
133+
event.studyId()
123134
);
124135
}
125136
}
126137
}
127138

128139
@EventListener
129140
public void handleStudyExtensionReminderEvent(StudyExtensionReminderEvent event) {
130-
StudyMember leader = studyMemberRepository.findByStudyIdAndRoleAndActivatedTrue(event.studyId(), StudyMemberRole.LEADER)
131-
.orElseThrow(() -> new BusinessException(STUDY_LEADER_NOT_FOUND));
141+
StudyMember leader = studyMemberRepository.findByStudyIdAndRoleAndActivatedTrue(
142+
event.studyId(), StudyMemberRole.LEADER)
143+
.orElseThrow(() -> new BusinessException(STUDY_LEADER_NOT_FOUND));
132144

133145
saveAlarm(
134-
leader.getUserId(),
135-
AlarmType.STUDY_EXTENSION_REMINDER,
136-
DomainType.STUDY,
137-
event.studyId()
146+
leader.getUserId(),
147+
AlarmType.STUDY_EXTENSION_REMINDER,
148+
DomainType.STUDY,
149+
event.studyId()
138150
);
139151
}
140152

141153
@EventListener
142154
public void handleTimeVotePeriodCreatedEvent(TimeVotePeriodCreatedEvent event) {
143-
List<StudyMember> members = studyMemberRepository.findAllByStudyIdAndActivatedTrue(event.studyId());
155+
List<StudyMember> members = studyMemberRepository.findAllByStudyIdAndActivatedTrue(
156+
event.studyId());
144157

145158
for (StudyMember member : members) {
146159
if (!member.getUserId().equals(event.excludedUserId())) {
@@ -154,13 +167,40 @@ public void handleTimeVotePeriodCreatedEvent(TimeVotePeriodCreatedEvent event) {
154167
}
155168
}
156169

170+
@EventListener
171+
public void handleApplicationEvent(ApplicationEvent event) {
172+
RecruitmentPost recruitmentPost = recruitmentPostRepository.findByIdAndActivatedTrue(event.recruitmentPostId())
173+
.orElseThrow(() -> new BusinessException(NOT_FOUND));
174+
175+
saveAlarm(
176+
recruitmentPost.getUserId(),
177+
AlarmType.STUDY_APPLICATION,
178+
DomainType.RECRUITMENT_POST,
179+
event.recruitmentPostId()
180+
);
181+
}
182+
183+
@EventListener
184+
public void handleApplicationStatusChangedEvent(ApplicationStatusChangedEvent event) {
185+
StudyApplication application = applicationRepository.findByIdAndActivatedTrue(event.applicationId())
186+
.orElseThrow(() -> new BusinessException(APPLICATION_NOT_FOUND));
187+
188+
saveAlarm(
189+
application.getUserId(),
190+
event.alarmType(),
191+
DomainType.STUDY_APPLICATION,
192+
application.getId()
193+
);
194+
}
195+
157196
private Alarm findValidAlarm(Long alarmId) {
158-
return alarmRepository.findById(alarmId).orElseThrow(() -> new NotFoundException(AlarmErrorCode.ALARM_NOT_FOUND));
197+
return alarmRepository.findById(alarmId)
198+
.orElseThrow(() -> new NotFoundException(AlarmErrorCode.ALARM_NOT_FOUND));
159199
}
160200

161201
private Study findValidStudy(Long studyId) {
162202
return studyRepository.findById(studyId)
163-
.orElseThrow(() -> new NotFoundException(STUDY_NOT_FOUND));
203+
.orElseThrow(() -> new NotFoundException(STUDY_NOT_FOUND));
164204
}
165205

166206
private void validateParticipantStudyLimit(Long userId) {
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
}

src/main/java/grep/neogulcoder/domain/groupchat/controller/GroupChatRestController.java

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

3+
import grep.neogulcoder.domain.groupchat.controller.dto.response.ChatMessagePagingResponse;
34
import grep.neogulcoder.domain.groupchat.controller.dto.response.GroupChatMessageResponseDto;
45
import grep.neogulcoder.domain.groupchat.service.GroupChatService;
56
import grep.neogulcoder.global.response.ApiResponse;
@@ -17,15 +18,13 @@ public class GroupChatRestController implements GroupChatRestSpecification {
1718
// 과거 채팅 메시지 페이징 조회 (무한 스크롤용)
1819
@Override
1920
@GetMapping("/study/{studyId}/messages")
20-
public ApiResponse<PageResponse<GroupChatMessageResponseDto>> getMessages(
21+
public ApiResponse<ChatMessagePagingResponse> getMessages(
2122
@PathVariable("studyId") Long studyId,
2223
@RequestParam(defaultValue = "0") int page,
2324
@RequestParam(defaultValue = "20") int size
2425
) {
2526
// 서비스에서 페이징된 메시지 조회
26-
PageResponse<GroupChatMessageResponseDto> pageResponse =
27-
groupChatService.getMessages(studyId, page, size);
28-
29-
return ApiResponse.success(pageResponse);
27+
ChatMessagePagingResponse response = groupChatService.getMessages(studyId, page, size);
28+
return ApiResponse.success(response);
3029
}
3130
}

src/main/java/grep/neogulcoder/domain/groupchat/controller/GroupChatRestSpecification.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
package grep.neogulcoder.domain.groupchat.controller;
22

3+
import grep.neogulcoder.domain.groupchat.controller.dto.response.ChatMessagePagingResponse;
34
import grep.neogulcoder.domain.groupchat.controller.dto.response.GroupChatMessageResponseDto;
45
import grep.neogulcoder.global.response.ApiResponse;
56
import grep.neogulcoder.global.response.PageResponse;
@@ -73,7 +74,7 @@ public interface GroupChatRestSpecification {
7374
"""
7475
)
7576

76-
ApiResponse<PageResponse<GroupChatMessageResponseDto>> getMessages(
77+
ApiResponse<ChatMessagePagingResponse> getMessages(
7778
@Parameter(description = "스터디 ID", example = "1")
7879
@PathVariable("studyId") Long studyId,
7980

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
package grep.neogulcoder.domain.groupchat.controller.dto.response;
2+
3+
import io.swagger.v3.oas.annotations.media.Schema;
4+
import lombok.Builder;
5+
import lombok.Getter;
6+
import org.springframework.data.domain.Page;
7+
8+
import java.util.List;
9+
10+
@Getter
11+
public class ChatMessagePagingResponse {
12+
13+
@Schema(description = "채팅 메시지 목록")
14+
private final List<GroupChatMessageResponseDto> content;
15+
16+
@Schema(description = "현재 페이지 번호", example = "0")
17+
private final int currentPage;
18+
19+
@Schema(description = "페이지 크기", example = "20")
20+
private final int size;
21+
22+
@Schema(description = "전체 페이지 수", example = "5")
23+
private final int totalPages;
24+
25+
@Schema(description = "전체 메시지 수", example = "100")
26+
private final long totalElements;
27+
28+
@Schema(description = "다음 페이지 존재 여부", example = "true")
29+
private final boolean hasNext;
30+
31+
@Builder
32+
private ChatMessagePagingResponse(Page<GroupChatMessageResponseDto> page) {
33+
this.content = page.getContent();
34+
this.currentPage = page.getNumber();
35+
this.size = page.getSize();
36+
this.totalPages = page.getTotalPages();
37+
this.totalElements = page.getTotalElements();
38+
this.hasNext = page.hasNext();
39+
}
40+
41+
public static ChatMessagePagingResponse of(Page<GroupChatMessageResponseDto> page) {
42+
return new ChatMessagePagingResponse(page);
43+
}
44+
}

src/main/java/grep/neogulcoder/domain/groupchat/repository/GroupChatMessageRepository.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,8 @@ public interface GroupChatMessageRepository extends JpaRepository<GroupChatMessa
1111

1212
// 채팅방(roomId)에 속한 메시지를 전송 시간 내림차순으로 페이징 조회
1313
@Query("SELECT m FROM GroupChatMessage m " +
14-
"WHERE m.groupChatRoom.roomId = :roomId " +
14+
"JOIN FETCH m.groupChatRoom r " +
15+
"WHERE r.roomId = :roomId " +
1516
"ORDER BY m.sentAt ASC")
1617
Page<GroupChatMessage> findMessagesByRoomIdAsc(@Param("roomId") Long roomId, Pageable pageable);
1718

0 commit comments

Comments
 (0)