Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat : 설정 따른 알림 전송 #140

Merged
merged 30 commits into from
Nov 8, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
30 commits
Select commit Hold shift + click to select a range
0ea0492
feat : 알림 설정 엔티티 생성
rladmstn Oct 25, 2024
b0a2561
refactor : 알림 세팅 필드명 수정
rladmstn Oct 26, 2024
29b0e2e
feat : 그룹 생성, 가입 시 NotificationSetting 추가
rladmstn Oct 26, 2024
a654ce1
feat : 알림 목록 조회 API 추가
rladmstn Oct 26, 2024
6123b3f
test : StudyGroupServiceTest에 NotificationSettingRepository 모킹 추가
rladmstn Oct 26, 2024
743d2f0
Merge branch 'develop' into feat/#122
rladmstn Oct 29, 2024
3295984
feat : NotificationMessage 케이스 별 알림 writing 추가
rladmstn Oct 29, 2024
3557b7d
Resolve conflict
rladmstn Nov 1, 2024
12d1345
feat : 알림 정보 조회 실패 예외 클래스 추가
rladmstn Nov 1, 2024
3c463ba
test : 문제 시작 할 때 알림 전송 시, 알림 설정 모킹 추가
rladmstn Nov 1, 2024
713a706
feat : 문제 시작 할 때 알림 전송 시, 알림 설정 로직 추가
rladmstn Nov 1, 2024
3f7c324
feat : 풀이 생성 시 설정에 따라 그룹 멤버들에게 알림 전송 로직 추가
rladmstn Nov 1, 2024
461d0b1
test : 풀이 생성 테스트 작성
rladmstn Nov 1, 2024
d624232
feat : 댓글 작성 시 설정 따른 알림 전송 로직 추가
rladmstn Nov 1, 2024
da51350
test : 댓글 작성 성공 시 설정 따른 알림 전송 테스트 작성
rladmstn Nov 1, 2024
b4cdeed
fix : 댓글 작성 시 알림 전송 메소드 오류 수정
rladmstn Nov 1, 2024
21bc04c
fix : 댓글 작성 시 알림 전송 메소드 오류 repository 수정
rladmstn Nov 1, 2024
0a7b7b1
test : CommentRepository 모킹 추가
rladmstn Nov 1, 2024
ea1b712
feat : 새로운 멤버 가입 시, 설정 따른 알림 전송 로직 추가
rladmstn Nov 1, 2024
8988326
test : 새로운 멤버 가입 시, 설정 따른 알림 전송 테스트 추가 작성
rladmstn Nov 1, 2024
2f29387
feat : 마감 날짜가 오늘인 문제들에 대해 설정 따른 알림 전송 로직 추가
rladmstn Nov 2, 2024
2b8646b
test : 마감 날짜가 오늘인 문제들에 대해 설정 따른 알림 전송 테스트 추가 작성
rladmstn Nov 2, 2024
9e8babe
feat : 그룹 제거 및 회원 탈퇴 시 notificationSetting 제거
rladmstn Nov 2, 2024
0e0dc5f
test : 그룹 삭제 시 테스트 일부 수정
rladmstn Nov 2, 2024
c539756
feat : 알림 설정 수정 API 추가
rladmstn Nov 3, 2024
b4461de
test : 알림 설정 관련 API 테스트 작성
rladmstn Nov 3, 2024
5a5dda3
Resolve conflict
rladmstn Nov 5, 2024
658cc2c
Resolve conflict
rladmstn Nov 6, 2024
a416c3b
refactor : 알림 전송 로직을 NotificationService에 위임
rladmstn Nov 6, 2024
67153a1
feat : 알림 전송할 때 알림 설정 조회 실패 시, error log 추가
rladmstn Nov 8, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
import com.gamzabat.algohub.feature.group.studygroup.exception.GroupMemberValidationException;
import com.gamzabat.algohub.feature.group.studygroup.exception.InvalidRoleException;
import com.gamzabat.algohub.feature.notice.exception.NoticeValidationException;
import com.gamzabat.algohub.feature.notification.exception.CannotFoundNotificationSettingException;
import com.gamzabat.algohub.feature.problem.exception.NotBojLinkException;
import com.gamzabat.algohub.feature.problem.exception.SolvedAcApiErrorException;
import com.gamzabat.algohub.feature.solution.exception.CannotFoundSolutionException;
Expand Down Expand Up @@ -121,4 +122,10 @@ protected ResponseEntity<ErrorResponse> handler(CannotFoundProblemException e) {
protected ResponseEntity<ErrorResponse> handler(CannotFoundRankingException e) {
return ResponseEntity.badRequest().body(new ErrorResponse(HttpStatus.NOT_FOUND.value(), e.getError(), null));
}

@ExceptionHandler(CannotFoundNotificationSettingException.class)
protected ResponseEntity<ErrorResponse> handler(CannotFoundNotificationSettingException e) {
return ResponseEntity.status(HttpStatus.NOT_FOUND)
.body(new ErrorResponse(HttpStatus.NOT_FOUND.value(), e.getError(), null));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -16,5 +16,4 @@ public interface CommentService<T extends CreateCommentRequest> {
void updateComment(User user, UpdateCommentRequest request);

void deleteComment(User user, Long commentId);

}
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,10 @@
import com.gamzabat.algohub.feature.group.studygroup.repository.GroupMemberRepository;
import com.gamzabat.algohub.feature.group.studygroup.repository.StudyGroupRepository;
import com.gamzabat.algohub.feature.image.service.ImageService;
import com.gamzabat.algohub.feature.notification.domain.NotificationSetting;
import com.gamzabat.algohub.feature.notification.enums.NotificationCategory;
import com.gamzabat.algohub.feature.notification.repository.NotificationSettingRepository;
import com.gamzabat.algohub.feature.notification.service.NotificationService;
import com.gamzabat.algohub.feature.problem.domain.Problem;
import com.gamzabat.algohub.feature.problem.repository.ProblemRepository;
import com.gamzabat.algohub.feature.solution.repository.SolutionRepository;
Expand All @@ -69,6 +73,8 @@ public class StudyGroupService {
private final RankingRepository rankingRepository;

private final ObjectProvider<StudyGroupService> studyGroupServiceProvider;
private final NotificationSettingRepository notificationSettingRepository;
private final NotificationService notificationService;

@Transactional
public GroupCodeResponse createGroup(User user, CreateGroupRequest request, MultipartFile profileImage) {
Expand Down Expand Up @@ -99,6 +105,10 @@ public GroupCodeResponse createGroup(User user, CreateGroupRequest request, Mult
.currentRank(1)
.rankDiff("-")
.build());

notificationSettingRepository.save(
NotificationSetting.builder().member(member).build()
);
log.info("success to save study group");
return new GroupCodeResponse(inviteCode);
}
Expand All @@ -119,6 +129,10 @@ public void joinGroupWithCode(User user, String code) {
.build();
groupMemberRepository.save(member);

notificationSettingRepository.save(
NotificationSetting.builder().member(member).build()
);

rankingRepository.save(Ranking.builder()
.member(member)
.currentRank(groupMemberRepository.countByStudyGroup(studyGroup))
Expand All @@ -127,6 +141,8 @@ public void joinGroupWithCode(User user, String code) {
.build()
);

sendNewMemberNotification(studyGroup, member);

log.info("success to join study group");
}

Expand All @@ -142,7 +158,8 @@ public void deleteGroup(User user, Long groupId) {
if (RoleOfGroupMember.isOwner(groupMember)) { // owner
bookmarkedStudyGroupRepository.deleteAll(bookmarkedStudyGroupRepository.findAllByStudyGroup(studyGroup));
rankingRepository.deleteAll(rankingRepository.findAllByStudyGroup(studyGroup));
groupMemberRepository.delete(groupMember);
notificationSettingRepository.deleteAll(notificationSettingRepository.findAllByStudyGroup(studyGroup));
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

이런거 그냥 studygroup으로 질의해서 batch delete하면 쿼리 2->1번으로 줄일 수 있겠네용. 반영해달라는건 아니고 사족입니다

groupMemberRepository.deleteAll(groupMemberRepository.findAllByStudyGroup(studyGroup));
groupRepository.delete(studyGroup);
} else { // member
studyGroupServiceProvider.getObject().deleteMemberFromStudyGroup(user, groupMember, studyGroup);
Expand Down Expand Up @@ -177,6 +194,7 @@ public void deleteMemberFromStudyGroup(User user, GroupMember groupMember, Study
bookmarkedStudyGroupRepository.findByUserAndStudyGroup(user, studyGroup)
.ifPresent(bookmarkedStudyGroupRepository::delete);
rankingRepository.deleteByMember(groupMember);
notificationSettingRepository.deleteByMember(groupMember);
groupMemberRepository.delete(groupMember);
log.info("success to delete group member");
}
Expand Down Expand Up @@ -432,4 +450,18 @@ public void editStudyGroupVisibility(User user, EditGroupVisibilityRequest reque
member.updateVisibility(request.isVisible());
log.info("success to update group visibility ( userId : {} )", user.getId());
}

private void sendNewMemberNotification(StudyGroup studyGroup, GroupMember newMember) {
List<GroupMember> members = groupMemberRepository.findAllByStudyGroup(studyGroup)
.stream()
.filter(member -> !member.getId().equals(newMember.getId()))
.toList();

notificationService.sendNotificationToMembers(
studyGroup,
members,
NotificationCategory.NEW_MEMBER_JOINED,
NotificationCategory.NEW_MEMBER_JOINED.getMessage(newMember.getUser().getNickname())
);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,20 +5,27 @@
import java.util.List;

import org.springframework.http.ResponseEntity;
import org.springframework.validation.Errors;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PatchMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestHeader;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.servlet.mvc.method.annotation.SseEmitter;

import com.gamzabat.algohub.common.annotation.AuthedUser;
import com.gamzabat.algohub.exception.RequestException;
import com.gamzabat.algohub.feature.notification.dto.EditNotificationSettingRequest;
import com.gamzabat.algohub.feature.notification.dto.GetNotificationResponse;
import com.gamzabat.algohub.feature.notification.dto.GetNotificationSettingResponse;
import com.gamzabat.algohub.feature.notification.service.NotificationService;
import com.gamzabat.algohub.feature.notification.service.NotificationSettingService;
import com.gamzabat.algohub.feature.user.domain.User;

import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.validation.Valid;
import lombok.RequiredArgsConstructor;

@RestController
Expand All @@ -27,6 +34,7 @@
@Tag(name = "알림 API", description = "알림 관련 API")
public class NotificationController {
private final NotificationService notificationService;
private final NotificationSettingService notificationSettingService;

@GetMapping(value = "/subscribe", produces = TEXT_EVENT_STREAM_VALUE)
@Operation(summary = "SSE 알림 연결 API")
Expand All @@ -47,4 +55,20 @@ public void updateIsRead(@AuthedUser User user) {
notificationService.updateIsRead(user);
}

@GetMapping(value = "/setting")
@Operation(summary = "알림 설정 목록 조회 API", description = "유저가 가입한 그룹들에 대해 알림 설정 목록을 조회하는 API")
public ResponseEntity<List<GetNotificationSettingResponse>> getNotificationSettings(@AuthedUser User user) {
return ResponseEntity.ok().body(notificationSettingService.getNotificationSettings(user));
}

@PatchMapping(value = "/setting")
@Operation(summary = "알림 설정 수정 API")
public ResponseEntity<Void> updateNotificationSettings(@AuthedUser User user, @Valid @RequestBody
EditNotificationSettingRequest request, Errors errors) {
if (errors.hasErrors())
throw new RequestException("알림 설정 수정 요청이 올바르지 않습니다.", errors);
notificationSettingService.editNotificationSettings(user, request);
return ResponseEntity.ok().build();
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
package com.gamzabat.algohub.feature.notification.domain;

import com.gamzabat.algohub.feature.group.studygroup.domain.GroupMember;

import jakarta.persistence.Entity;
import jakarta.persistence.FetchType;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.GenerationType;
import jakarta.persistence.Id;
import jakarta.persistence.JoinColumn;
import jakarta.persistence.OneToOne;
import lombok.Builder;
import lombok.Getter;
import lombok.NoArgsConstructor;

@Entity
@Getter
@NoArgsConstructor
public class NotificationSetting {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;

@OneToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "member_id")
private GroupMember member;

private boolean allNotifications;
private boolean newProblem;
private boolean newSolution;
private boolean newComment;
private boolean newMember;
private boolean deadlineReached;

@Builder
public NotificationSetting(GroupMember member) {
this.member = member;
this.allNotifications = true;
this.newProblem = true;
this.newSolution = true;
this.newComment = true;
this.newMember = true;
this.deadlineReached = true;
}

public void editSettings(boolean all, boolean newProblem, boolean newSolution, boolean newComment,
boolean newMember, boolean deadlineReached) {
this.allNotifications = all;
this.newProblem = newProblem;
this.newSolution = newSolution;
this.newComment = newComment;
this.newMember = newMember;
this.deadlineReached = deadlineReached;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package com.gamzabat.algohub.feature.notification.dto;

import jakarta.validation.constraints.NotNull;

public record EditNotificationSettingRequest(@NotNull(message = "그룹 아이디는 필수 입력입니다.") Long groupId,
boolean all,
boolean newProblem,
boolean newSolution,
boolean newComment,
boolean newMember,
boolean deadlineReached) {
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
package com.gamzabat.algohub.feature.notification.dto;

import com.gamzabat.algohub.feature.notification.domain.NotificationSetting;

import lombok.Builder;

@Builder
public record GetNotificationSettingResponse(Long groupId,
String groupName,
boolean allNotifications,
boolean newProblem,
boolean newSolution,
boolean newComment,
boolean newMember,
boolean deadlineReached) {

public static GetNotificationSettingResponse toDTO(NotificationSetting setting) {
return GetNotificationSettingResponse.builder()
.groupId(setting.getMember().getStudyGroup().getId())
.groupName(setting.getMember().getStudyGroup().getName())
.allNotifications(setting.isAllNotifications())
.newProblem(setting.isNewProblem())
.newSolution(setting.isNewSolution())
.newComment(setting.isNewComment())
.newMember(setting.isNewMember())
.deadlineReached(setting.isDeadlineReached())
.build();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
package com.gamzabat.algohub.feature.notification.enums;

public enum NotificationCategory {
PROBLEM_STARTED("[%s] 문제가 시작되었습니다! 지금 도전해보세요!"),
NEW_SOLUTION_POSTED("%s님이 새로운 풀이를 등록했습니다! 풀이를 확인하고 의견을 나눠보세요."),
NEW_MEMBER_JOINED("%s님이 스터디에 합류했습니다!"),
NEW_COMMENT_POSTED("%s님이 내 풀이에 코멘트를 남겼습니다! 어떤 리뷰인지 확인해보세요."),
PROBLEM_DEADLINE_REACHED("[%s] 문제의 마감이 오늘입니다! 아직 해결하지 못했다면 지금 도전해보세요!");

private final String message;

NotificationCategory(String message) {
this.message = message;
}

public String getMessage(Object... args) {
return String.format(message, args);
}
}

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package com.gamzabat.algohub.feature.notification.exception;

import lombok.Getter;

@Getter
public class CannotFoundNotificationSettingException extends RuntimeException {
private final String error;

public CannotFoundNotificationSettingException(String error) {
this.error = error;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
package com.gamzabat.algohub.feature.notification.repository;

import java.util.Optional;

import org.springframework.data.jpa.repository.JpaRepository;

import com.gamzabat.algohub.feature.group.studygroup.domain.GroupMember;
import com.gamzabat.algohub.feature.notification.domain.NotificationSetting;
import com.gamzabat.algohub.feature.notification.repository.querydsl.CustomNotificationSettingRepository;

public interface NotificationSettingRepository extends JpaRepository<NotificationSetting, Long>,
CustomNotificationSettingRepository {

Optional<NotificationSetting> findByMember(GroupMember member);

void deleteByMember(GroupMember member);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package com.gamzabat.algohub.feature.notification.repository.querydsl;

import java.util.List;

import com.gamzabat.algohub.feature.group.studygroup.domain.StudyGroup;
import com.gamzabat.algohub.feature.notification.domain.NotificationSetting;
import com.gamzabat.algohub.feature.user.domain.User;

public interface CustomNotificationSettingRepository {
List<NotificationSetting> findAllByUser(User user);

List<NotificationSetting> findAllByStudyGroup(StudyGroup studyGroup);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
package com.gamzabat.algohub.feature.notification.repository.querydsl;

import static com.gamzabat.algohub.feature.notification.domain.QNotificationSetting.*;

import java.util.List;

import org.springframework.stereotype.Repository;

import com.gamzabat.algohub.feature.group.studygroup.domain.StudyGroup;
import com.gamzabat.algohub.feature.notification.domain.NotificationSetting;
import com.gamzabat.algohub.feature.user.domain.User;
import com.querydsl.jpa.impl.JPAQueryFactory;

import lombok.AllArgsConstructor;

@Repository
@AllArgsConstructor
public class CustomNotificationSettingRepositoryImpl implements CustomNotificationSettingRepository {
private final JPAQueryFactory query;

@Override
public List<NotificationSetting> findAllByUser(User user) {
return query.selectFrom(notificationSetting)
.where(notificationSetting.member.user.eq(user))
.fetch();
}

@Override
public List<NotificationSetting> findAllByStudyGroup(StudyGroup studyGroup) {
return query.selectFrom(notificationSetting)
.where(notificationSetting.member.studyGroup.eq(studyGroup))
.fetch();
}
}
Loading
Loading