diff --git a/src/main/java/com/sesac/boheommong/BoheommongApplication.java b/src/main/java/com/sesac/boheommong/BoheommongApplication.java index 6c9ca03..ca73093 100644 --- a/src/main/java/com/sesac/boheommong/BoheommongApplication.java +++ b/src/main/java/com/sesac/boheommong/BoheommongApplication.java @@ -3,7 +3,9 @@ import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.context.annotation.PropertySource; +import org.springframework.scheduling.annotation.EnableScheduling; +@EnableScheduling @SpringBootApplication public class BoheommongApplication { diff --git a/src/main/java/com/sesac/boheommong/domain/notification/repository/EmitterRepositoryImpl.java b/src/main/java/com/sesac/boheommong/domain/notification/repository/EmitterRepositoryImpl.java index c62d68f..f1014a9 100644 --- a/src/main/java/com/sesac/boheommong/domain/notification/repository/EmitterRepositoryImpl.java +++ b/src/main/java/com/sesac/boheommong/domain/notification/repository/EmitterRepositoryImpl.java @@ -34,7 +34,7 @@ public Map findAllEmitterByUserId(String userId) { @Override public Map findAllEventCacheByUserId(String userId) { - return emitters.entrySet().stream() + return eventCache.entrySet().stream() .filter(entry -> entry.getKey().startsWith(userId)) .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue)); } diff --git a/src/main/java/com/sesac/boheommong/domain/tosspayment/controller/WidgetController.java b/src/main/java/com/sesac/boheommong/domain/tosspayment/controller/WidgetController.java index ac71258..83c42a7 100644 --- a/src/main/java/com/sesac/boheommong/domain/tosspayment/controller/WidgetController.java +++ b/src/main/java/com/sesac/boheommong/domain/tosspayment/controller/WidgetController.java @@ -1,5 +1,7 @@ package com.sesac.boheommong.domain.tosspayment.controller; +import com.sesac.boheommong.domain.notification.service.NotificationService; +import lombok.RequiredArgsConstructor; import net.minidev.json.JSONObject; import net.minidev.json.parser.JSONParser; import net.minidev.json.parser.ParseException; @@ -21,10 +23,13 @@ import java.util.Base64; @Controller +@RequiredArgsConstructor public class WidgetController { private final Logger logger = LoggerFactory.getLogger(this.getClass()); + private final NotificationService notificationService; + @RequestMapping(value = "/confirm") public ResponseEntity confirmPayment(@RequestBody String jsonBody) throws Exception { @@ -75,6 +80,28 @@ public ResponseEntity confirmPayment(@RequestBody String jsonBody) t JSONObject jsonObject = (JSONObject) parser.parse(reader); responseStream.close(); + // (3) 결제가 성공했다면 SSE 알림 발송 + if (isSuccess) { + logger.info("결제 성공: orderId={}, amount={}", orderId, amount); + + // 예: orderId로 사용자 이메일이나 userId를 DB에서 찾아오거나, + // 일단 임시로 하드코딩한 이메일로 알림 전송: + String userEmail = "dummyUser@somewhere.com"; + + // 알림 메시지 예시 + String content = String.format("결제가 성공적으로 완료되었습니다. 결제금액: %s원", amount); + + // SSE 알림 발행 + notificationService.send( + userEmail, // 기존의 SSE 구독이 userEmail로 되어 있다면 이 값 사용 + null, // 알림 타입 (사용한다면 ENUM 넣기) + content, + null // 클릭 시 이동할 URL + ); + } else { + logger.warn("결제 실패: code={}, response={}", code, jsonObject); + } + return ResponseEntity.status(code).body(jsonObject); } } diff --git a/src/main/java/com/sesac/boheommong/domain/tosspayment/repository/AutoPaymentRepository.java b/src/main/java/com/sesac/boheommong/domain/tosspayment/repository/AutoPaymentRepository.java index b909454..9807b4c 100644 --- a/src/main/java/com/sesac/boheommong/domain/tosspayment/repository/AutoPaymentRepository.java +++ b/src/main/java/com/sesac/boheommong/domain/tosspayment/repository/AutoPaymentRepository.java @@ -13,4 +13,6 @@ public interface AutoPaymentRepository extends JpaRepository * AutoPayment 엔티티 내부의 user 필드(연관관계) → user.userId */ List findByUser_UserId(Long userId); + + List findByDayOfMonth(int dayOfMonth); } diff --git a/src/main/java/com/sesac/boheommong/domain/tosspayment/scheduler/AutoPaymentScheduler.java b/src/main/java/com/sesac/boheommong/domain/tosspayment/scheduler/AutoPaymentScheduler.java new file mode 100644 index 0000000..74cf7df --- /dev/null +++ b/src/main/java/com/sesac/boheommong/domain/tosspayment/scheduler/AutoPaymentScheduler.java @@ -0,0 +1,60 @@ +package com.sesac.boheommong.domain.tosspayment.scheduler; + +import com.sesac.boheommong.domain.tosspayment.entity.AutoPayment; +import com.sesac.boheommong.domain.tosspayment.repository.AutoPaymentRepository; +import com.sesac.boheommong.domain.notification.service.NotificationService; +import com.sesac.boheommong.domain.notification.enums.NotificationType; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.scheduling.annotation.Scheduled; +import org.springframework.stereotype.Component; + +import java.time.LocalDate; +import java.time.LocalTime; +import java.util.List; + +@Component +@RequiredArgsConstructor +@Slf4j +public class AutoPaymentScheduler { + + private final AutoPaymentRepository autoPaymentRepository; + private final NotificationService notificationService; + + /** + * 매일 오전 0시(혹은 원하는 시각)에 실행 + * - “내일 자동결제 예정”인 AutoPayment 찾아서, 알림 발행 + */ + @Scheduled(cron = "0 0 0 * * *") + public void notifyBeforeAutoPayment() { + log.info("자동결제 전날 알림 스케줄러 실행..."); + + // (1) 오늘 날짜 계산 + LocalDate today = LocalDate.now(); + + // (2) "내일" 자동이체가 예정된 AutoPayment 찾기 + // - dayOfMonth == tomorrow's day + // - time은 결제 시각이지만, 전날 알림이므로 여기서는 dayOfMonth만 체크 + int tomorrowDay = today.plusDays(1).getDayOfMonth(); + + // dayOfMonth가 tomorrowDay인 AutoPayment 목록 (DB 쿼리 방식은 상황에 따라 다름) + List autoPayments = autoPaymentRepository.findByDayOfMonth(tomorrowDay); + + // (3) 알림 발행 + for (AutoPayment ap : autoPayments) { + Long userId = ap.getUser().getUserId(); + + // 메시지 예시 + String content = "내일 (" + tomorrowDay + "일) 자동결제가 예정되어 있습니다. (상품ID: " + ap.getProduct().getProductId() + ")"; + + // Redis를 통해 발행(멀티 서버) + notificationService.publishNotification( + userId, + content, + "/mypage/autoPayment" // 알림 클릭 시 이동할 URL (예시) + ); + + log.info("자동결제 전날 알림 발행 - userId={}, autoPaymentId={}", userId, ap.getId()); + } + } +} diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml index f943ae5..577d067 100644 --- a/src/main/resources/application.yml +++ b/src/main/resources/application.yml @@ -40,7 +40,7 @@ spring: client-id: ${KAKAO_OAUTH_CLIENT_ID} client-secret: ${KAKAO_OAUTH_CLIENT_SECRET} authorization-grant-type: authorization_code - redirect-uri: https://boheommong.site/login/oauth2/code/kakao + redirect-uri: http://localhost:8080/login/oauth2/code/kakao client-authentication-method: client_secret_post scope: - profile_nickname