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] 회원 알림 설정 수정 API 구현 #198

Merged
merged 49 commits into from
Feb 7, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
49 commits
Select commit Hold shift + click to select a range
c2cbd10
#187 feat: 회원 알림 모두 읽음 컨트롤러 및 서비스 구현
yonghwankim-dev Feb 6, 2024
bd3af8c
#187 test: 알림 모두 읽기 테스트 코드 추가
yonghwankim-dev Feb 6, 2024
a9947d6
#187 test: 알림 모두 읽기 예외 테스트 코드 추가
yonghwankim-dev Feb 6, 2024
7e0ea22
#187 feat: 존재하지 않는 알람 등록번호 검증 메소드 추가
yonghwankim-dev Feb 6, 2024
183d385
#187 test: 알림 모두 읽음 서비스 예외 테스트 추가
yonghwankim-dev Feb 6, 2024
b6cb5a6
#187 test: 알림 특정 읽음 컨트롤러 테스트 코드 추가
yonghwankim-dev Feb 6, 2024
d4f7a19
#187 feat: 알림 전체 삭제 컨트롤러 및 서비스 구현
yonghwankim-dev Feb 6, 2024
b8e1926
#187 test: 알림 전체 삭제 컨트롤러 및 서비스 테스트 코드 구현
yonghwankim-dev Feb 6, 2024
d2261bf
#187 feat: 특정 알림 삭제 컨트롤러 메소드 구현
yonghwankim-dev Feb 6, 2024
221213d
#187 test: 특정 알림 삭제 컨트롤러 테스트 코드 구현
yonghwankim-dev Feb 6, 2024
4d25f5e
#187 fix: isDeletd 조건문 추가
yonghwankim-dev Feb 6, 2024
56eeba4
#187 docs: notification 스키마 수정
yonghwankim-dev Feb 6, 2024
c161a38
#187 feat: 회원 알림 설정 수정 컨트롤러 및 서비스 구현
yonghwankim-dev Feb 7, 2024
2c83f86
#187 test: 회원 알림 설정 수정 서비스 테스트 코드 구현
yonghwankim-dev Feb 7, 2024
4d39450
#187 test: 회원 알림 설정 수정 컨트롤러 테스트 코드 구현
yonghwankim-dev Feb 7, 2024
e491889
#187 fix: soft delete 제거
yonghwankim-dev Feb 7, 2024
2235c9e
#187 feat: 소셜 로그인 또는 회원가입시 기본 알림 설정 등록하도록 구현
yonghwankim-dev Feb 7, 2024
bf61fd9
#187 feat: 회원 알림 설정 수정관련 서비스 테스트 코드 추가
yonghwankim-dev Feb 7, 2024
fe9bddf
#187 fix: validation 애노테이션 추가
yonghwankim-dev Feb 7, 2024
610a747
[feat] 회원 알림 관련 API 구현 (#197)
yonghwankim-dev Feb 6, 2024
33413fd
#187 feat: 회원 알림 모두 읽음 컨트롤러 및 서비스 구현
yonghwankim-dev Feb 6, 2024
986499b
#187 test: 알림 모두 읽기 테스트 코드 추가
yonghwankim-dev Feb 6, 2024
6a1f040
#187 feat: 존재하지 않는 알람 등록번호 검증 메소드 추가
yonghwankim-dev Feb 6, 2024
22a68af
#187 feat: 알림 전체 삭제 컨트롤러 및 서비스 구현
yonghwankim-dev Feb 6, 2024
3ffe0ea
#187 feat: 회원 알림 모두 읽음 컨트롤러 및 서비스 구현
yonghwankim-dev Feb 6, 2024
2b4fda6
#187 test: 알림 모두 읽기 테스트 코드 추가
yonghwankim-dev Feb 6, 2024
a99c251
#187 test: 알림 모두 읽기 예외 테스트 코드 추가
yonghwankim-dev Feb 6, 2024
0e05984
#187 feat: 존재하지 않는 알람 등록번호 검증 메소드 추가
yonghwankim-dev Feb 6, 2024
fbefa7e
#187 test: 알림 모두 읽음 서비스 예외 테스트 추가
yonghwankim-dev Feb 6, 2024
a73bc75
#187 test: 알림 특정 읽음 컨트롤러 테스트 코드 추가
yonghwankim-dev Feb 6, 2024
3c83426
#187 feat: 알림 전체 삭제 컨트롤러 및 서비스 구현
yonghwankim-dev Feb 6, 2024
4e0824a
#187 test: 알림 전체 삭제 컨트롤러 및 서비스 테스트 코드 구현
yonghwankim-dev Feb 6, 2024
1709370
#187 feat: 특정 알림 삭제 컨트롤러 메소드 구현
yonghwankim-dev Feb 6, 2024
5fe37c0
#187 test: 특정 알림 삭제 컨트롤러 테스트 코드 구현
yonghwankim-dev Feb 6, 2024
f4c64d2
#187 fix: isDeletd 조건문 추가
yonghwankim-dev Feb 6, 2024
2a5be38
#187 feat: 회원 알림 설정 수정 컨트롤러 및 서비스 구현
yonghwankim-dev Feb 7, 2024
9535741
#187 test: 회원 알림 설정 수정 서비스 테스트 코드 구현
yonghwankim-dev Feb 7, 2024
6ef85e1
#187 test: 회원 알림 설정 수정 컨트롤러 테스트 코드 구현
yonghwankim-dev Feb 7, 2024
1693f1c
#187 fix: soft delete 제거
yonghwankim-dev Feb 7, 2024
e33f300
#187 feat: 소셜 로그인 또는 회원가입시 기본 알림 설정 등록하도록 구현
yonghwankim-dev Feb 7, 2024
49df611
#187 feat: 회원 알림 설정 수정관련 서비스 테스트 코드 추가
yonghwankim-dev Feb 7, 2024
55defd7
#187 fix: validation 애노테이션 추가
yonghwankim-dev Feb 7, 2024
a22f228
[feat] 회원 알림 관련 API 구현 (#197)
yonghwankim-dev Feb 6, 2024
f93f05e
#187 feat: 회원 알림 모두 읽음 컨트롤러 및 서비스 구현
yonghwankim-dev Feb 6, 2024
6f0e896
#187 test: 알림 모두 읽기 테스트 코드 추가
yonghwankim-dev Feb 6, 2024
1236e9a
#187 feat: 존재하지 않는 알람 등록번호 검증 메소드 추가
yonghwankim-dev Feb 6, 2024
6ddc7e1
#187 feat: 알림 전체 삭제 컨트롤러 및 서비스 구현
yonghwankim-dev Feb 6, 2024
896dba2
Merge remote-tracking branch 'origin/feat/#187-members-notification-2…
yonghwankim-dev Feb 7, 2024
021cb1d
#187 fix: deleteAllBatch cnrk
yonghwankim-dev Feb 7, 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 @@ -8,10 +8,10 @@

public interface NotificationRepository extends JpaRepository<Notification, Long> {

@Query("select n from Notification n where n.member.id = :memberId and n.isDeleted = false order by n.createAt desc")
@Query("select n from Notification n where n.member.id = :memberId order by n.createAt desc")
List<Notification> findAllByMemberId(@Param("memberId") Long memberId);

@Query("select n from Notification n where n.member.id = :memberId and n.isDeleted = false and n.id in (:notificationIds)")
@Query("select n from Notification n where n.member.id = :memberId and n.id in (:notificationIds)")
List<Notification> findAllByMemberIdAndIds(
@Param("memberId") Long memberId,
@Param("notificationIds") List<Long> notificationIds
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -45,4 +45,21 @@ public NotificationPreference(Member member, boolean browserNotify, boolean targ
this.maxLossNotify = maxLossNotify;
this.targetPriceNotify = targetPriceNotify;
}

public static NotificationPreference defaultSetting(Member member) {
return NotificationPreference.builder()
.browserNotify(false)
.targetGainNotify(true)
.maxLossNotify(true)
.targetPriceNotify(true)
.member(member)
.build();
}

public void changePreference(NotificationPreference notificationPreference) {
this.browserNotify = notificationPreference.browserNotify;
this.targetGainNotify = notificationPreference.targetGainNotify;
this.maxLossNotify = notificationPreference.maxLossNotify;
this.targetPriceNotify = notificationPreference.targetPriceNotify;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package codesquad.fineants.domain.notification_preference;

import java.util.Optional;

import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.query.Param;

public interface NotificationPreferenceRepository extends JpaRepository<NotificationPreference, Long> {

@Query("select n from NotificationPreference n where n.member.id = :memberId")
Optional<NotificationPreference> findByMemberId(@Param("memberId") Long memberId);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
package codesquad.fineants.spring.api.errors.errorcode;

import org.springframework.http.HttpStatus;

import lombok.Getter;
import lombok.RequiredArgsConstructor;

@Getter
@RequiredArgsConstructor
public enum NotificationPreferenceErrorCode implements ErrorCode {

NOT_FOUND_NOTIFICATION_PREFERENCE(HttpStatus.NOT_FOUND, "존재하지 않는 알림 설정입니다");

private final HttpStatus httpStatus;
private final String message;

@Override
public String toString() {
return String.format("%s, %s(name=%s, httpStatus=%s, message=%s)", "알림 설정 에러 코드",
this.getClass().getSimpleName(),
name(),
httpStatus,
message);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,6 @@ public ApiResponse<Void> readAllNotifications(
return ApiResponse.success(MemberSuccessCode.OK_FETCH_ALL_NOTIFICATIONS);
}

// 회원의 알림 특정 읽기
@PatchMapping("/notifications/{notificationId}")
public ApiResponse<Void> readNotification(
@PathVariable Long memberId,
Expand All @@ -67,16 +66,4 @@ public ApiResponse<Void> deleteAllNotifications(
log.info("회원 알림 모두 삭제 처리 결과 : memberId={}, 삭제한 알림 등록 번호={}", memberId, deletedNotificationIds);
return ApiResponse.success(MemberSuccessCode.OK_DELETED_ALL_NOTIFICATIONS);
}

@DeleteMapping("/notifications/{notificationId}")
public ApiResponse<Void> deleteNotification(
@PathVariable Long memberId,
@PathVariable Long notificationId) {
List<Long> deletedNotificationIds = notificationService.deleteAllNotifications(
memberId,
List.of(notificationId)
);
log.info("회원 알림 모두 삭제 처리 결과 : memberId={}, 삭제한 알림 등록 번호={}", memberId, deletedNotificationIds);
return ApiResponse.success(MemberSuccessCode.OK_DELETD_NOTIFICATION);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
package codesquad.fineants.spring.api.member.request;

import javax.validation.constraints.NotNull;

import codesquad.fineants.domain.notification_preference.NotificationPreference;
import lombok.AccessLevel;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Getter;
import lombok.NoArgsConstructor;

@Getter
@NoArgsConstructor(access = AccessLevel.PRIVATE)
@AllArgsConstructor(access = AccessLevel.PRIVATE)
@Builder
public class MemberNotificationPreferenceRequest {
@NotNull(message = "필수 정보입니다")
private Boolean browserNotify;
@NotNull(message = "필수 정보입니다")
private Boolean targetGainNotify;
@NotNull(message = "필수 정보입니다")
private Boolean maxLossNotify;
@NotNull(message = "필수 정보입니다")
private Boolean targetPriceNotify;

public NotificationPreference toEntity() {
return NotificationPreference.builder()
.browserNotify(browserNotify)
.targetGainNotify(targetGainNotify)
.maxLossNotify(maxLossNotify)
.targetPriceNotify(targetPriceNotify)
.build();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
package codesquad.fineants.spring.api.member.response;

import codesquad.fineants.domain.notification_preference.NotificationPreference;
import lombok.AccessLevel;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.ToString;

@Getter
@NoArgsConstructor(access = AccessLevel.PRIVATE)
@AllArgsConstructor(access = AccessLevel.PRIVATE)
@Builder
@ToString
public class MemberNotificationPreferenceResponse {
private Boolean browserNotify;
private Boolean targetGainNotify;
private Boolean maxLossNotify;
private Boolean targetPriceNotify;

public static MemberNotificationPreferenceResponse from(NotificationPreference notificationPreference) {
return MemberNotificationPreferenceResponse.builder()
.browserNotify(notificationPreference.isBrowserNotify())
.targetGainNotify(notificationPreference.isTargetGainNotify())
.maxLossNotify(notificationPreference.isMaxLossNotify())
.targetPriceNotify(notificationPreference.isTargetPriceNotify())
.build();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
package codesquad.fineants.spring.api.member.service;

import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import codesquad.fineants.domain.member.Member;
import codesquad.fineants.domain.notification_preference.NotificationPreference;
import codesquad.fineants.domain.notification_preference.NotificationPreferenceRepository;
import codesquad.fineants.spring.api.errors.errorcode.NotificationPreferenceErrorCode;
import codesquad.fineants.spring.api.errors.exception.NotFoundResourceException;
import codesquad.fineants.spring.api.member.request.MemberNotificationPreferenceRequest;
import codesquad.fineants.spring.api.member.response.MemberNotificationPreferenceResponse;
import lombok.RequiredArgsConstructor;

@Service
@RequiredArgsConstructor
@Transactional(readOnly = true)
public class MemberNotificationPreferenceService {

private final NotificationPreferenceRepository notificationPreferenceRepository;

@Transactional
public MemberNotificationPreferenceResponse registerDefaultNotificationPreference(Member member) {
NotificationPreference preference = notificationPreferenceRepository.findByMemberId(member.getId())
.orElseGet(() -> NotificationPreference.defaultSetting(member));
NotificationPreference saveNotificationPreference = notificationPreferenceRepository.save(preference);
return MemberNotificationPreferenceResponse.from(saveNotificationPreference);
}

@Transactional
public MemberNotificationPreferenceResponse updateNotificationPreference(
Long memberId,
MemberNotificationPreferenceRequest request) {
NotificationPreference preference = findNotificationPreference(memberId);
preference.changePreference(request.toEntity());
return MemberNotificationPreferenceResponse.from(preference);
}

private NotificationPreference findNotificationPreference(Long memberId) {
return notificationPreferenceRepository.findByMemberId(memberId)
.orElseThrow(() ->
new NotFoundResourceException(NotificationPreferenceErrorCode.NOT_FOUND_NOTIFICATION_PREFERENCE));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,7 @@ public class MemberService {
private final PortfolioGainHistoryRepository portfolioGainHistoryRepository;
private final PurchaseHistoryRepository purchaseHistoryRepository;
private final VerifyCodeGenerator verifyCodeGenerator;
private final MemberNotificationPreferenceService preferenceService;

public OauthMemberLoginResponse login(OauthMemberLoginRequest request) {
log.info("로그인 서비스 요청 : loginRequest={}", request);
Expand All @@ -100,6 +101,7 @@ public OauthMemberLoginResponse login(OauthMemberLoginRequest request) {
.supplyAsync(supplyOauthClient(provider))
.thenApply(fetchProfile(request))
.thenCompose(saveMember(provider))
.thenCompose(registerNotificationPreference())
.thenApply(createLoginResponse(request.getRequestTime()));

try {
Expand Down Expand Up @@ -138,6 +140,17 @@ private Supplier<Member> supplyMember(String provider, OauthUserProfile profile)
.orElseGet(() -> memberFactory.createMember(profile));
}

private Function<Member, CompletionStage<Member>> registerNotificationPreference() {
return member -> {
saveDefaultNotificationPreference(member);
return CompletableFuture.supplyAsync(() -> member);
};
}

private void saveDefaultNotificationPreference(Member member) {
preferenceService.registerDefaultNotificationPreference(member);
}

private Optional<Member> findMember(String email, String provider) {
return memberRepository.findMemberByEmailAndProvider(email, provider);
}
Expand Down Expand Up @@ -209,6 +222,8 @@ public SignUpServiceResponse signup(SignUpServiceRequest request) {
encryptedPassword
)
);
saveDefaultNotificationPreference(member);

log.info("일반 회원가입 결과 : {}", member);
return SignUpServiceResponse.from(member);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,7 @@ public enum MemberSuccessCode implements SuccessCode {
OK_LOGIN(HttpStatus.OK, "로그인에 성공하였습니다."),
OK_READ_NOTIFICATIONS(HttpStatus.OK, "현재 알림 목록 조회를 성공했습니다"),
OK_FETCH_ALL_NOTIFICATIONS(HttpStatus.OK, "알림을 모두 읽음 처리했습니다"),
OK_DELETED_ALL_NOTIFICATIONS(HttpStatus.OK, "알림 전체 삭제를 성공하였습니다"),
OK_DELETD_NOTIFICATION(HttpStatus.OK, "알림 삭제를 성공하였습니다");
OK_DELETED_ALL_NOTIFICATIONS(HttpStatus.OK, "알림 전체 삭제를 성공하였습니다");

private final HttpStatus httpStatus;
private final String message;
Expand Down
4 changes: 4 additions & 0 deletions src/main/resources/db/mysql/data.sql
Original file line number Diff line number Diff line change
Expand Up @@ -88,3 +88,7 @@ VALUES (1, '2024-01-22 10:10:10.0', '지정가', '삼성전자가 지정가 KRW6

INSERT INTO fineAnts.notification(id, create_at, title, content, is_read, type, reference_id, member_id)
VALUES (2, '2024-01-23 10:10:10.0', '포트폴리오', '내꿈은 워렌버핏의 목표 수익률을 달성했습니다', false, 'portfolio', '1', 1);

INSERT INTO fineAnts.notification_preference(id, create_at, browser_notify, max_loss_notify, target_gain_notify,
target_price_notify, member_id)
VALUES (1, now(), false, true, true, true, 1);
Loading