Skip to content

[Feat/#101] 누락된 푸시 알림 구현 - #104

Merged
yunmi-dev merged 2 commits into
developfrom
feat/#101-push-notifications
Apr 5, 2026
Merged

yunmi-dev merged 2 commits into
developfrom
feat/#101-push-notifications

Conversation

@yunmi-dev

@yunmi-dev yunmi-dev commented Apr 5, 2026 •

Copy link
Copy Markdown
Collaborator

📌 관련 이슈

✨ 변경 사항

  • 13시 할 일 미완료 1차 알림 추가 (ALL)
  • 12시 습관 미완료 1차 알림 추가 (ALL)
  • 일요일 20시 성장탭 확인 알림 추가 (자녀/부모)
  • 습관 기간 만료 시 자녀 알림 추가 - "보상을 받을 수 있어요. 지금 바로 확인해 볼까요?"
  • 습관 기간 만료 시 부모 알림 추가 - "자녀가 습관을 완료했어요. 보상을 줄 시간이에요!"

📚 리뷰어 참고 사항

  • 부모 습관 등록 시 자녀 알림은 문구 논의 중으로 미포함
  • 스티커 판 완성 알림은 후순위로 미포함
  • HabitScheduler에 NotificationService, FamilyRelationService 의존성 추가

✅ 체크리스트

  • 브랜치 전략(git flow)을 따랐나요?
  • 로컬에서 빌드 및 실행이 정상적으로 되나요?
  • 불필요한 주석이나 더미 코드는 제거했나요?
  • 컨벤션(커밋 메시지, 코드 스타일)을 지켰나요?
  • spotlessApply 실행했나요?

Summary by CodeRabbit

  • New Features
    • Push notifications now sent to parents and children when habits expire
    • New daily push notification at 13:00 reminding users of incomplete todos
    • New daily push notification at 12:00 reminding users of incomplete habits
    • New weekly push notification every Sunday at 20:00 for growth tracking and confirmation
    • Enhanced notifications sent to children when parents manage or view habit rewards

@coderabbitai

coderabbitai Bot commented Apr 5, 2026 •

Copy link
Copy Markdown
📝 Walkthrough

Walkthrough

These changes implement missing push notification features including scheduled reminders for incomplete todos and habits, weekly growth confirmations, and notification flows triggered when habit periods expire or parents view reward information.

Changes

Cohort / File(s) Summary
Habit Expiration Notifications
src/main/java/com/swyp/server/domain/habit/repository/HabitRepository.java, src/main/java/com/swyp/server/domain/habit/service/HabitScheduler.java
Added findExpiredHabits() repository method with eager user loading. Extended scheduler to retrieve expired habits, partition notifications by user type (child/parent), query connected family members for parent IDs, and send targeted notifications before updating habit statuses.
Reward Notification Flow
src/main/java/com/swyp/server/domain/habit/service/HabitService.java
Integrated post-commit notification logic in getHabitRewards() to send "new reward" messages to children when a parent user accesses the reward listing, using transaction synchronization.
Scheduled Push Notifications
src/main/java/com/swyp/server/global/notification/PushNotificationScheduler.java
Added three new cron-scheduled notification jobs: daily 13:00 todo-incomplete reminders (all users), daily 12:00 habit-incomplete reminders (all users), and weekly Sunday 20:00 growth-tab confirmations (partitioned by user type).

Sequence Diagram(s)

sequenceDiagram
    participant Scheduler as HabitScheduler
    participant Repo as HabitRepository
    participant NotifSvc as NotificationService
    participant FamilyRel as FamilyRelationService
    
    Scheduler->>Repo: findExpiredHabits(now)
    Repo-->>Scheduler: List<Habit> with users
    
    loop For each expired habit
        alt Child user detected
            Scheduler->>NotifSvc: sendToUser(childId, childMsg)
            NotifSvc-->>Scheduler: ✓
            Scheduler->>FamilyRel: getConnectedMembers(childId)
            FamilyRel-->>Scheduler: List of members
            alt Parent members exist
                Scheduler->>NotifSvc: sendToUsers(parentIds, parentMsg)
                NotifSvc-->>Scheduler: ✓
            end
        end
    end
    
    Scheduler->>Repo: updateExpiredHabitsStatus(now)
    Repo-->>Scheduler: Update complete
Loading
sequenceDiagram
    participant Service as HabitService
    participant Repo as HabitRepository
    participant TxnSync as TransactionSynchronization
    participant NotifSvc as NotificationService
    
    Service->>Repo: Query habits by reward status
    Repo-->>Service: Habit list
    Service->>Service: Compute connected children IDs
    
    alt Children exist
        Service->>TxnSync: Register afterCommit callback
        TxnSync-->>Service: Registered
        
        Note over Service: Method returns
        
        Note over TxnSync: After transaction commits
        TxnSync->>NotifSvc: sendToUsers(childIds, "newReward")
        NotifSvc-->>TxnSync: ✓
    end
Loading
sequenceDiagram
    participant Scheduler as PushNotificationScheduler
    participant UserSvc as UserService
    participant HabitRepo as HabitRepository
    participant TodoRepo as TodoRepository
    participant NotifSvc as NotificationService
    
    Scheduler->>UserSvc: findAllActiveUsers()
    UserSvc-->>Scheduler: List<User>
    Scheduler->>Scheduler: Extract user IDs
    
    par Todo Reminder at 13:00
        Scheduler->>TodoRepo: findIncompleteByDate(users, today)
        TodoRepo-->>Scheduler: Incomplete todos
        alt Non-empty result
            Scheduler->>NotifSvc: sendToUsers(userIds, todoMsg)
            NotifSvc-->>Scheduler: ✓
        end
    and Habit Reminder at 12:00
        Scheduler->>HabitRepo: findIncompleteHabits(users)
        HabitRepo-->>Scheduler: Incomplete habits
        alt Non-empty result
            Scheduler->>NotifSvc: sendToUsers(userIds, habitMsg)
            NotifSvc-->>Scheduler: ✓
        end
    and Weekly Growth at Sunday 20:00
        Scheduler->>Scheduler: Partition users (CHILD/PARENT)
        alt Children non-empty
            Scheduler->>NotifSvc: sendToUsers(childIds, childGrowthMsg)
        end
        alt Parents non-empty
            Scheduler->>NotifSvc: sendToUsers(parentIds, parentGrowthMsg)
        end
    end
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related PRs

Suggested reviewers

  • SeHyeonCho
  • guzzang

Poem

🐰 Notifications hop and dance with glee,
Reminders bloom where habits used to be,
Expired fields now send their gentle chime,
Children and parents sync in perfect time—
A push-notif symphony, timed just right! 🌙✨

🚥 Pre-merge checks | ✅ 3 | ❌ 2

❌ Failed checks (1 warning, 1 inconclusive)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 36.36% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
Linked Issues check ❓ Inconclusive The PR implements 3 of 5 required objectives from #101: scheduled notifications for todos (13:00), habits (12:00), and weekly growth (Sunday 20:00), plus expired habit notifications in HabitScheduler. However, 2 objectives remain incomplete: habit expiration notifications lack user-facing implementation. Verify that expired habit notifications for both child and parent users are fully implemented in HabitScheduler.checkExpiredHabits() before merge.
✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title '[Feat/#101] 누락된 푸시 알림 구현' directly references the linked issue and summarizes the main feature: implementing missing push notifications.
Out of Scope Changes check ✅ Passed All changes are scoped to push notification implementation in #101: new scheduler methods, HabitRepository query, HabitScheduler notification logic, and HabitService reward notifications align with stated objectives.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/#101-push-notifications

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🧹 Nitpick comments (1)
src/main/java/com/swyp/server/domain/habit/service/HabitScheduler.java (1)

36-59: Consider batching family relation lookups to avoid N+1 queries.

The current implementation calls familyRelationService.getConnectedMembers() for each expired CHILD habit (line 47-48). If many habits expire simultaneously, this results in N additional database queries.

Consider collecting all child user IDs first, then fetching family relations in a single batch query, to improve performance under load.

♻️ Sketch of batched approach
-        expiredHabits.stream()
-                .filter(h -> h.getUser().getUserType() == UserType.CHILD)
-                .forEach(
-                        h -> {
-                            notificationService.sendToUser(
-                                    h.getUser().getId(),
-                                    "해봄",
-                                    "보상을 받을 수 있어요. 지금 바로 확인해 볼까요?",
-                                    Map.of());
-
-                            List<Long> parentIds =
-                                    familyRelationService
-                                            .getConnectedMembers(h.getUser().getId())
-                                            .stream()
-                                            .filter(m -> m.getUserType() == UserType.PARENT)
-                                            .map(User::getId)
-                                            .toList();
-                            if (!parentIds.isEmpty()) {
-                                notificationService.sendToUsers(
-                                        parentIds, "해봄", "자녀가 습관을 완료했어요. 보상을 줄 시간이에요!", Map.of());
-                            }
-                        });
+        List<Long> childUserIds = expiredHabits.stream()
+                .filter(h -> h.getUser().getUserType() == UserType.CHILD)
+                .map(h -> h.getUser().getId())
+                .distinct()
+                .toList();
+
+        // Send to children
+        notificationService.sendToUsers(
+                childUserIds, "해봄", "보상을 받을 수 있어요. 지금 바로 확인해 볼까요?", Map.of());
+
+        // Batch fetch parent relations and notify (requires new batch method in FamilyRelationService)
+        // Or iterate if batch not available:
+        Set<Long> parentIds = new HashSet<>();
+        for (Long childId : childUserIds) {
+            familyRelationService.getConnectedMembers(childId).stream()
+                    .filter(m -> m.getUserType() == UserType.PARENT)
+                    .map(User::getId)
+                    .forEach(parentIds::add);
+        }
+        if (!parentIds.isEmpty()) {
+            notificationService.sendToUsers(
+                    new ArrayList<>(parentIds), "해봄", "자녀가 습관을 완료했어요. 보상을 줄 시간이에요!", Map.of());
+        }

Note: The sketch above also deduplicates child notifications (one per child vs one per habit) and parent notifications. Verify whether sending one notification per child or per habit is the intended behavior.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/main/java/com/swyp/server/domain/habit/service/HabitScheduler.java`
around lines 36 - 59, The expiredHabits stream in HabitScheduler currently calls
familyRelationService.getConnectedMembers() per habit causing N+1 queries;
instead collect all child user IDs from expiredHabits (filtering by
UserType.CHILD), call familyRelationService.getConnectedMembers(...) once with
the set/list of child IDs to fetch relations in batch, map those results to
parent ID lists keyed by child ID, then iterate the expired children to send
notifications using notificationService.sendToUser/sendToUsers while
deduplicating parentIds per child (and optionally deduplicating across children
if you intend one parent notification), and finally call
habitRepository.updateExpiredHabitsStatus(now) as before.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@src/main/java/com/swyp/server/domain/habit/service/HabitService.java`:
- Around line 110-126: The notification block in HabitService.getHabitRewards is
incorrectly sending "new reward" notifications on every read and also
re-computes childIds; remove the
TransactionSynchronization.registerSynchronization(...) block (the afterCommit()
notification call that uses notificationService.sendToUsers) from the
getHabitRewards/read flow so viewing rewards no longer triggers notifications,
and eliminate the redundant childIds computation in that method; if
notifications are needed when a reward is actually created, add the
TransactionSynchronization/notificationService.sendToUsers logic to the write
path (e.g., createHabit or the reward-creation method) instead, referencing the
same notificationService and childIds resolution there.

---

Nitpick comments:
In `@src/main/java/com/swyp/server/domain/habit/service/HabitScheduler.java`:
- Around line 36-59: The expiredHabits stream in HabitScheduler currently calls
familyRelationService.getConnectedMembers() per habit causing N+1 queries;
instead collect all child user IDs from expiredHabits (filtering by
UserType.CHILD), call familyRelationService.getConnectedMembers(...) once with
the set/list of child IDs to fetch relations in batch, map those results to
parent ID lists keyed by child ID, then iterate the expired children to send
notifications using notificationService.sendToUser/sendToUsers while
deduplicating parentIds per child (and optionally deduplicating across children
if you intend one parent notification), and finally call
habitRepository.updateExpiredHabitsStatus(now) as before.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 26551dd4-b966-4e2a-b623-8c89912efb25

📥 Commits

Reviewing files that changed from the base of the PR and between cff8f95 and 49052f4.

📒 Files selected for processing (4)
  • src/main/java/com/swyp/server/domain/habit/repository/HabitRepository.java
  • src/main/java/com/swyp/server/domain/habit/service/HabitScheduler.java
  • src/main/java/com/swyp/server/domain/habit/service/HabitService.java
  • src/main/java/com/swyp/server/global/notification/PushNotificationScheduler.java

Comment thread src/main/java/com/swyp/server/domain/habit/service/HabitService.java Outdated
}
});

habitRepository.updateExpiredHabitsStatus(now);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

P3:
로직 자체는 문제 없는 거 같은데 00시에 알림 보내도 괜찮을까요...??

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

저도 00시 알림이 부자연스러워 보여서 걱정이 되었는데, 일단 기획서 기준으로는 만료 즉시 알림이 맞아서 스케줄러 실행 시간인 00시에 보내도록 구현했습니다..! PM분께 추가 확인 해보고 추후 수정해볼게요!

@yunmi-dev
yunmi-dev merged commit 5927975 into develop Apr 5, 2026
1 check passed
@yunmi-dev
yunmi-dev deleted the feat/#101-push-notifications branch April 5, 2026 11:39
@coderabbitai coderabbitai Bot mentioned this pull request Apr 15, 2026
5 tasks done
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Feat] 누락된 푸시 알림 구현

2 participants