Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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 @@ -39,6 +39,10 @@ List<Habit> findAllByUserIdAndStatusOrderByIsCompletedAscIdDesc(
List<Habit> findAllByUserIdsAndStatusOptional(
@Param("userIds") List<Long> userIds, @Param("status") RewardStatus status);

@EntityGraph(attributePaths = "user")
@Query("SELECT h FROM Habit h WHERE h.status = 'IN_PROGRESS' AND h.expiredAt < :now")
List<Habit> findExpiredHabits(@Param("now") LocalDateTime now);

@Modifying(clearAutomatically = true)
@Query(
"UPDATE Habit h "
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,15 @@
package com.swyp.server.domain.habit.service;

import com.swyp.server.domain.family.service.FamilyRelationService;
import com.swyp.server.domain.habit.entity.Habit;
import com.swyp.server.domain.habit.repository.HabitRepository;
import com.swyp.server.domain.user.entity.User;
import com.swyp.server.domain.user.entity.UserType;
import com.swyp.server.global.notification.NotificationService;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.util.List;
import java.util.Map;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.scheduling.annotation.Scheduled;
Expand All @@ -15,12 +22,41 @@
public class HabitScheduler {

private final HabitRepository habitRepository;
private final NotificationService notificationService;
private final FamilyRelationService familyRelationService;

@Transactional
@Scheduled(cron = "0 0 0 * * *", zone = "Asia/Seoul")
public void checkExpiredHabits() {
log.info("만료된 습관 체크 스케줄러 시작");
habitRepository.updateExpiredHabitsStatus(LocalDateTime.now(ZoneId.of("Asia/Seoul")));
LocalDateTime now = LocalDateTime.now(ZoneId.of("Asia/Seoul"));

List<Habit> expiredHabits = habitRepository.findExpiredHabits(now);

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());
}
});

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분께 추가 확인 해보고 추후 수정해볼게요!

}

@Transactional
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -78,4 +78,57 @@ public void sendHabitNotRegisteredNotification() {
targetIds, "해봄", "아직 등록된 습관이 없어요. 지금 습관을 추가해 볼까요?", Map.of());
log.info("Habit not registered notification sent to {} users", targetIds.size());
}

// 매일 13시 - 할 일 미완료 1차 (모든 유저)
@Scheduled(cron = "0 0 13 * * *", zone = "Asia/Seoul")
public void sendTodoIncompleteFirstNotification() {
LocalDate today = LocalDate.now(SEOUL_ZONE);
List<User> users = userRepository.findAllActive();
List<Long> userIds = users.stream().map(User::getId).toList();
if (userIds.isEmpty()) return;
List<Long> targetIds = todoRepository.findUserIdsWithIncompleteTodo(userIds, today);
notificationService.sendToUsers(
targetIds, "해봄", "아직 완료하지 않은 할 일이 있어요. 지금 확인해 볼까요?", Map.of());
log.info("Todo incomplete 1st notification sent to {} users", targetIds.size());
}

// 매일 12시 - 습관 미완료 1차 (모든 유저)
@Scheduled(cron = "0 0 12 * * *", zone = "Asia/Seoul")
public void sendHabitIncompleteFirstNotification() {
List<User> users = userRepository.findAllActive();
List<Long> userIds = users.stream().map(User::getId).toList();
if (userIds.isEmpty()) return;
List<Long> targetIds = habitRepository.findUserIdsWithIncompleteHabit(userIds);
notificationService.sendToUsers(
targetIds, "해봄", "아직 완료하지 않은 습관이 있어요. 지금 바로 실천해 볼까요?", Map.of());
log.info("Habit incomplete 1st notification sent to {} users", targetIds.size());
}

// 매주 일요일 20시 - 성장탭 확인 알림
@Scheduled(cron = "0 0 20 * * SUN", zone = "Asia/Seoul")
public void sendWeeklyGrowthNotification() {
List<User> users = userRepository.findAllActive();
List<Long> childIds =
users.stream()
.filter(u -> u.getUserType() == UserType.CHILD)
.map(User::getId)
.toList();
List<Long> parentIds =
users.stream()
.filter(u -> u.getUserType() == UserType.PARENT)
.map(User::getId)
.toList();
if (!childIds.isEmpty()) {
notificationService.sendToUsers(
childIds, "해봄", "이번 주가 마무리 됐어요. 나의 성장을 확인해 보세요!", Map.of());
}
if (!parentIds.isEmpty()) {
notificationService.sendToUsers(
parentIds, "해봄", "이번 주가 마무리 됐어요. 자녀의 성장을 확인해 보세요!", Map.of());
}
log.info(
"Weekly growth notification sent to {} children, {} parents",
childIds.size(),
parentIds.size());
}
}