Skip to content

Commit

Permalink
[fix] 회원 알람 설정 수정 스펙 변경 (#222)
Browse files Browse the repository at this point in the history
* #220 docs: 스키마 변경

* #220 feat: 회원 알람 설정 수정시 모든 상태가 비활성화인 경우 FCM 토큰을 제거하도록 변경

* #220 test: 회원 알람 설정 수정 관련 테스트 코드 추가
  • Loading branch information
yonghwankim-dev authored Feb 16, 2024
1 parent 5261ee7 commit ab51cc6
Show file tree
Hide file tree
Showing 8 changed files with 145 additions and 3 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -14,4 +14,8 @@ public interface FcmRepository extends JpaRepository<FcmToken, Long> {
@Modifying
@Query("delete from FcmToken f where f.token in (:tokens)")
int deleteAllByTokens(@Param("tokens") List<String> tokens);

@Modifying
@Query("delete from FcmToken f where f.id = :fcmTokenId")
int deleteByFcmTokenId(@Param("fcmTokenId") Long fcmTokenId);
}
Original file line number Diff line number Diff line change
Expand Up @@ -62,4 +62,11 @@ public void changePreference(NotificationPreference notificationPreference) {
this.maxLossNotify = notificationPreference.maxLossNotify;
this.targetPriceNotify = notificationPreference.targetPriceNotify;
}

public boolean isAllInActive() {
return !this.browserNotify
&& !this.targetGainNotify
&& !this.maxLossNotify
&& !this.targetPriceNotify;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
package codesquad.fineants.spring.api.fcm.response;

import lombok.AccessLevel;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.ToString;

@ToString
@Getter
@NoArgsConstructor(access = AccessLevel.PRIVATE)
@AllArgsConstructor(access = AccessLevel.PRIVATE)
@Builder
public class FcmDeleteResponse {
private Long fcmTokenId;

public static FcmDeleteResponse from(Long fcmTokenId) {
return FcmDeleteResponse.builder()
.fcmTokenId(fcmTokenId)
.build();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
import codesquad.fineants.spring.api.errors.exception.BadRequestException;
import codesquad.fineants.spring.api.errors.exception.NotFoundResourceException;
import codesquad.fineants.spring.api.fcm.request.FcmRegisterRequest;
import codesquad.fineants.spring.api.fcm.response.FcmDeleteResponse;
import codesquad.fineants.spring.api.fcm.response.FcmRegisterResponse;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
Expand Down Expand Up @@ -58,4 +59,11 @@ private Member findMember(AuthMember authMember) {
return memberRepository.findById(authMember.getMemberId())
.orElseThrow(() -> new NotFoundResourceException(MemberErrorCode.NOT_FOUND_MEMBER));
}

@Transactional
public FcmDeleteResponse deleteToken(Long fcmTokenId) {
int deleteCount = fcmRepository.deleteByFcmTokenId(fcmTokenId);
log.info("FCM 토큰 삭제 개수 : deleteCount={}", deleteCount);
return FcmDeleteResponse.from(fcmTokenId);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ public class MemberNotificationPreferenceRequest {
private Boolean maxLossNotify;
@NotNull(message = "필수 정보입니다")
private Boolean targetPriceNotify;
private Long fcmTokenId;

public NotificationPreference toEntity() {
return NotificationPreference.builder()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,17 +10,22 @@
import codesquad.fineants.spring.api.errors.errorcode.MemberErrorCode;
import codesquad.fineants.spring.api.errors.errorcode.NotificationPreferenceErrorCode;
import codesquad.fineants.spring.api.errors.exception.NotFoundResourceException;
import codesquad.fineants.spring.api.fcm.response.FcmDeleteResponse;
import codesquad.fineants.spring.api.fcm.service.FcmService;
import codesquad.fineants.spring.api.member.request.MemberNotificationPreferenceRequest;
import codesquad.fineants.spring.api.member.response.MemberNotificationPreferenceResponse;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;

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

private final NotificationPreferenceRepository notificationPreferenceRepository;
private final MemberRepository memberRepository;
private final FcmService fcmService;

@Transactional
public MemberNotificationPreferenceResponse registerDefaultNotificationPreference(Member member) {
Expand All @@ -44,6 +49,12 @@ public MemberNotificationPreferenceResponse updateNotificationPreference(
NotificationPreference preference = notificationPreferenceRepository.findByMemberId(memberId)
.orElseThrow(() ->
new NotFoundResourceException(NotificationPreferenceErrorCode.NOT_FOUND_NOTIFICATION_PREFERENCE));

// 회원 계정의 전체 알림 설정이 모두 비활성화인 경우 FCM 토큰 삭제
if (preference.isAllInActive() && request.getFcmTokenId() != null) {
FcmDeleteResponse response = fcmService.deleteToken(request.getFcmTokenId());
log.info("회원 알림 설정 전체 비활성화로 인한 결과 : {}", response);
}
return MemberNotificationPreferenceResponse.from(preference);
}
}
18 changes: 15 additions & 3 deletions src/main/resources/db/mysql/init-schema.sql
Original file line number Diff line number Diff line change
Expand Up @@ -170,17 +170,29 @@ create table if not exists fineAnts.stock_target_price
primary key,
create_at datetime(6) null,
modified_at datetime(6) null,
target_price bigint null,
is_active bit null,
member_id bigint null,
ticker_symbol varchar(255) null,
constraint UKb01jhjkq681lpyfjx5glikxee
unique (member_id, ticker_symbol, target_price),
constraint UKhwlfu5x3iqpei19soxhmjcfs3
unique (member_id, ticker_symbol),
constraint FK2r0grp1n205hnw3ysp179f5l3
foreign key (member_id) references fineAnts.member (id),
constraint FKcup8hchscft8jniri3wkk72kx
foreign key (ticker_symbol) references fineAnts.stock (ticker_symbol)
);

create table if not exists fineAnts.target_price_notification
(
id bigint auto_increment
primary key,
create_at datetime(6) null,
modified_at datetime(6) null,
target_price bigint null,
stock_target_price_id bigint null,
constraint FKnds69ucw684g4c2a09g0fa5bq
foreign key (stock_target_price_id) references fineAnts.stock_target_price (id)
);

create table if not exists fineAnts.notification
(
id bigint auto_increment
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import static org.assertj.core.api.Assertions.*;
import static org.junit.jupiter.api.Assertions.*;

import java.time.LocalDateTime;
import java.util.List;

import org.junit.jupiter.api.AfterEach;
Expand All @@ -12,6 +13,8 @@
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.ActiveProfiles;

import codesquad.fineants.domain.fcm_token.FcmRepository;
import codesquad.fineants.domain.fcm_token.FcmToken;
import codesquad.fineants.domain.member.Member;
import codesquad.fineants.domain.member.MemberRepository;
import codesquad.fineants.domain.notification_preference.NotificationPreference;
Expand All @@ -32,8 +35,12 @@ class MemberNotificationPreferenceServiceTest {
@Autowired
private NotificationPreferenceRepository repository;

@Autowired
private FcmRepository fcmRepository;

@AfterEach
void tearDown() {
fcmRepository.deleteAllInBatch();
repository.deleteAllInBatch();
memberRepository.deleteAllInBatch();
}
Expand Down Expand Up @@ -128,6 +135,75 @@ void updateNotificationPreference_whenNotExistPreference_thenRegisterPreference(
);
}

@DisplayName("사용자는 회원 알림 설정 수정시 설정값을 모두 비활성화하는 경우 FcmToken을 제거하도록 합니다")
@Test
void updateNotificationPreference_whenPreferenceIsAllInActive_thenDeleteFcmToken() {
// given
Member member = memberRepository.save(createMember());
FcmToken fcmToken = fcmRepository.save(createFcmToken(member));
repository.save(createNotificationPreference(member));
MemberNotificationPreferenceRequest request = MemberNotificationPreferenceRequest.builder()
.browserNotify(false)
.targetGainNotify(false)
.maxLossNotify(false)
.targetPriceNotify(false)
.fcmTokenId(fcmToken.getId())
.build();

// when
MemberNotificationPreferenceResponse response = service.updateNotificationPreference(member.getId(), request);

// then
NotificationPreference preference = repository.findByMemberId(member.getId()).orElseThrow();
assertAll(
() -> assertThat(response)
.extracting("browserNotify", "targetGainNotify", "maxLossNotify", "targetPriceNotify")
.containsExactly(false, false, false, false),
() -> assertThat(preference)
.extracting("browserNotify", "targetGainNotify", "maxLossNotify", "targetPriceNotify")
.containsExactly(false, false, false, false),
() -> assertThat(fcmRepository.findById(fcmToken.getId()).isEmpty()).isTrue()
);
}

@DisplayName("사용자는 푸시 알림을 허용하지 않은 상태(FCM 토큰 미등록 상태)에서 회원 알람 설정 수정시 FCM 토큰을 삭제하지 않는다")
@Test
void updateNotificationPreference_whenPreferenceIsAllInActiveAndFcmTokenIsNotStored_thenNotDeleteFcmToken() {
// given
Member member = memberRepository.save(createMember());
repository.save(createNotificationPreference(member));
MemberNotificationPreferenceRequest request = MemberNotificationPreferenceRequest.builder()
.browserNotify(false)
.targetGainNotify(false)
.maxLossNotify(false)
.targetPriceNotify(false)
.fcmTokenId(null)
.build();

// when
MemberNotificationPreferenceResponse response = service.updateNotificationPreference(member.getId(), request);

// then
NotificationPreference preference = repository.findByMemberId(member.getId()).orElseThrow();
assertAll(
() -> assertThat(response)
.extracting("browserNotify", "targetGainNotify", "maxLossNotify", "targetPriceNotify")
.containsExactly(false, false, false, false),
() -> assertThat(preference)
.extracting("browserNotify", "targetGainNotify", "maxLossNotify", "targetPriceNotify")
.containsExactly(false, false, false, false),
() -> assertThat(fcmRepository.findAllByMemberId(member.getId()).isEmpty()).isTrue()
);
}

private FcmToken createFcmToken(Member member) {
return FcmToken.builder()
.token("token")
.latestActivationTime(LocalDateTime.now())
.member(member)
.build();
}

private Member createMember() {
return Member.builder()
.nickname("일개미1234")
Expand Down

0 comments on commit ab51cc6

Please sign in to comment.