Skip to content

Commit 694aae8

Browse files
authored
Merge pull request #156 from DevKor-github/hotfix/notification-setting
hotfix(notify): 알림 설정 미생성으로 인한 작성 리마인더 알림 미발송 수정
2 parents 7751e36 + 5c1ec34 commit 694aae8

5 files changed

Lines changed: 76 additions & 1 deletion

File tree

src/main/java/com/devkor/ifive/nadab/domain/notification/application/NotificationSettingService.java

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
import com.devkor.ifive.nadab.global.core.response.ErrorCode;
1010
import com.devkor.ifive.nadab.global.exception.NotFoundException;
1111
import lombok.RequiredArgsConstructor;
12+
import lombok.extern.slf4j.Slf4j;
1213
import org.springframework.stereotype.Service;
1314
import org.springframework.transaction.annotation.Transactional;
1415

@@ -24,6 +25,7 @@
2425
@Service
2526
@RequiredArgsConstructor
2627
@Transactional
28+
@Slf4j
2729
public class NotificationSettingService {
2830

2931
private final NotificationSettingRepository notificationSettingRepository;
@@ -67,6 +69,26 @@ public List<NotificationSetting> getSettings(Long userId) {
6769
.toList();
6870
}
6971

72+
/**
73+
* 없는 그룹의 알림 설정을 기본값으로 생성(디바이스 등록 시)
74+
*/
75+
public void ensureSettingsExist(User user) {
76+
Set<NotificationGroup> existingGroups = notificationSettingRepository.findByUser(user)
77+
.stream()
78+
.map(NotificationSetting::getGroup)
79+
.collect(Collectors.toSet());
80+
81+
for (NotificationGroup group : NotificationGroup.values()) {
82+
if (!existingGroups.contains(group)) {
83+
try {
84+
notificationSettingRepository.save(NotificationSetting.create(user, group));
85+
} catch (DataIntegrityViolationException e) {
86+
log.debug("NotificationSetting already exists (race condition): userId={}, group={}", user.getId(), group);
87+
}
88+
}
89+
}
90+
}
91+
7092
/**
7193
* 여러 그룹의 알림 설정 일괄 수정
7294
* - 1개 이상의 그룹 설정을 한 번에 수정

src/main/java/com/devkor/ifive/nadab/domain/notification/application/UserDeviceCommandService.java

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ public class UserDeviceCommandService {
2323

2424
private final UserDeviceRepository userDeviceRepository;
2525
private final UserRepository userRepository;
26+
private final NotificationSettingService notificationSettingService;
2627

2728
/**
2829
* FCM 토큰 등록
@@ -62,13 +63,15 @@ public boolean registerDevice(
6263
UserDevice device = existingDevice.get();
6364
device.updateToken(fcmToken);
6465
log.debug("Device token updated: userId={}, platform={}", userId, platform);
66+
notificationSettingService.ensureSettingsExist(user);
6567
return false; // 기존 디바이스 업데이트
6668
} else {
6769
// 새 디바이스 등록
6870
try {
6971
UserDevice newDevice = UserDevice.create(user, fcmToken, deviceId, platform);
7072
userDeviceRepository.save(newDevice);
7173
log.debug("Device registered: userId={}, platform={}", userId, platform);
74+
notificationSettingService.ensureSettingsExist(user);
7275
return true; // 새 디바이스 등록
7376
} catch (DataIntegrityViolationException e) {
7477
// 동시성으로 인해 이미 생성된 경우 (Race Condition)
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
package com.devkor.ifive.nadab.domain.notification.application.scheduler;
2+
3+
import com.devkor.ifive.nadab.domain.notification.application.NotificationSettingService;
4+
import com.devkor.ifive.nadab.domain.notification.core.repository.UserDeviceRepository;
5+
import com.devkor.ifive.nadab.domain.user.core.entity.User;
6+
import lombok.RequiredArgsConstructor;
7+
import lombok.extern.slf4j.Slf4j;
8+
import org.springframework.boot.ApplicationArguments;
9+
import org.springframework.boot.ApplicationRunner;
10+
import org.springframework.stereotype.Component;
11+
12+
import java.util.List;
13+
14+
/**
15+
* 알림 설정 백필 러너
16+
* - FCM 디바이스가 등록된 유저 중 알림 설정이 없는 유저에게 기본값으로 생성
17+
* - 백필 완료 후 삭제 예정
18+
*/
19+
@Component
20+
@RequiredArgsConstructor
21+
@Slf4j
22+
public class NotificationSettingBackfillRunner implements ApplicationRunner {
23+
24+
private final UserDeviceRepository userDeviceRepository;
25+
private final NotificationSettingService notificationSettingService;
26+
27+
@Override
28+
public void run(ApplicationArguments args) {
29+
try {
30+
List<User> usersWithDevices = userDeviceRepository.findDistinctActiveUsers();
31+
32+
int count = 0;
33+
for (User user : usersWithDevices) {
34+
try {
35+
notificationSettingService.ensureSettingsExist(user);
36+
count++;
37+
} catch (Exception e) {
38+
log.error("Failed to initialize notification settings: userId={}", user.getId(), e);
39+
}
40+
}
41+
42+
log.info("NotificationSetting backfill completed: {}/{} users processed", count, usersWithDevices.size());
43+
} catch (Exception e) {
44+
log.error("NotificationSetting backfill runner failed", e);
45+
}
46+
}
47+
}

src/main/java/com/devkor/ifive/nadab/domain/notification/core/entity/NotificationSetting.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,7 @@ public static NotificationSetting create(User user, NotificationGroup group) {
5656

5757
// ACTIVITY_REMINDER 그룹이면 기본값 설정
5858
if (group == NotificationGroup.ACTIVITY_REMINDER) {
59-
setting.dailyWriteTime = LocalTime.of(20, 0);
59+
setting.dailyWriteTime = LocalTime.of(8, 0);
6060
}
6161

6262
return setting;

src/main/java/com/devkor/ifive/nadab/domain/notification/core/repository/UserDeviceRepository.java

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,9 @@ public interface UserDeviceRepository extends JpaRepository<UserDevice, Long> {
2424

2525
void deleteByFcmToken(String fcmToken);
2626

27+
@Query("SELECT DISTINCT ud.user FROM UserDevice ud WHERE ud.user.deletedAt IS NULL")
28+
List<User> findDistinctActiveUsers();
29+
2730
@Modifying(clearAutomatically = true, flushAutomatically = true)
2831
@Query("delete from UserDevice ud where ud.fcmToken in :fcmTokens")
2932
int deleteByFcmTokenIn(@Param("fcmTokens") List<String> fcmTokens);

0 commit comments

Comments
 (0)