diff --git a/src/main/java/com/api/trip/common/scheduler/SchedulerRunner.java b/src/main/java/com/api/trip/common/scheduler/SchedulerRunner.java index 7255e6b..aaa1966 100644 --- a/src/main/java/com/api/trip/common/scheduler/SchedulerRunner.java +++ b/src/main/java/com/api/trip/common/scheduler/SchedulerRunner.java @@ -6,8 +6,12 @@ import com.api.trip.common.naverapi.dto.ShoppingItem; import com.api.trip.common.naverapi.dto.ShoppingRequest; import com.api.trip.common.naverapi.dto.ShoppingResponse; +import com.api.trip.common.sse.SseNotificationResponse; +import com.api.trip.common.sse.emitter.SseEmitterMap; import com.api.trip.domain.item.controller.dto.CreateItemRequest; +import com.api.trip.domain.item.model.Item; import com.api.trip.domain.item.service.ItemService; +import com.api.trip.domain.notification.service.NotificationService; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.dao.DataIntegrityViolationException; @@ -27,7 +31,8 @@ public class SchedulerRunner { private final NaverClient naverClient; private final NaverApiService naverApiService; private final ItemService itemService; - + private final NotificationService notificationService; + private final SseEmitterMap sseEmitterMap; /** * @Description * 5분에 한번씩 api를 요청하여 데이터 갱신(중단조건에 걸릴때까지 50개씩 반복요청) @@ -49,7 +54,33 @@ public void updateData() List shoppingItems = naverApiService.doFilterCategory(searchResponse.getItems()); List createItemRequests = naverApiService.toCreateItemRequest(shoppingItems); for (CreateItemRequest createItemRequest : createItemRequests) { - itemService.createItem(createItemRequest); + Item item = itemService.createItem(createItemRequest); + notificationService.createNotification(item, createItemRequest.getTagNames()); + sseEmitterMap.sendToAll("notification",new SseNotificationResponse(item.getId(), createItemRequest.getTagNames())); + /** + * + * 알림이 가져야할 데이터가 itemId, memberId + * SSE보낼 때는 Object로 태그 넘기기 + */ + /** + * 백 + * 1. 아이템의 태그를 다 뽑아옴 + * 2. 태그를 구독한 member를 다 뽑아오는 로직 + * 3. 회원이 10000 -> "제주" + * + * 알림들의 id를 넘겨줄텐데 + * + * server || + * + * member1:"제주" -> 1 + * member2:"제주" -> 2 + * member3:"제주" -> 3 + * member4:"제주" -> 4 + * + * member1브라우저 || member1:"제주" -> tagNames = ["제주","서울], ids = [ 1,2,3,4] + * member2브라우저 || member1:"제주" -> 1,2,3,4 + */ + } i++; @@ -63,5 +94,7 @@ public void updateData() log.info("요청 범위 초과 - 데이터 갱신 종료"); } + + } } diff --git a/src/main/java/com/api/trip/common/security/oauth/OAuthSuccessHandler.java b/src/main/java/com/api/trip/common/security/oauth/OAuthSuccessHandler.java index 8f3e953..83e27bc 100644 --- a/src/main/java/com/api/trip/common/security/oauth/OAuthSuccessHandler.java +++ b/src/main/java/com/api/trip/common/security/oauth/OAuthSuccessHandler.java @@ -17,6 +17,7 @@ import org.springframework.security.oauth2.core.user.OAuth2User; import org.springframework.security.web.authentication.SimpleUrlAuthenticationSuccessHandler; import org.springframework.stereotype.Component; +import org.springframework.web.util.UriComponentsBuilder; import java.io.IOException; import java.util.Optional; @@ -58,12 +59,16 @@ public void onAuthenticationSuccess(HttpServletRequest request, HttpServletRespo // OAuth2User 객체에서 권한 가져옴 JwtToken jwtToken = jwtTokenProvider.createJwtToken(member.getEmail(), member.getRole().getValue()); - response.addHeader(HttpHeaders.SET_COOKIE, createCookie("accessToken", jwtToken.getAccessToken())); - response.addHeader(HttpHeaders.SET_COOKIE, createCookie("refreshToken", jwtToken.getRefreshToken())); - response.addHeader(HttpHeaders.SET_COOKIE, createCookie("memberId", String.valueOf(member.getId()))); - response.addHeader(HttpHeaders.SET_COOKIE, createCookie("profileImgUrl", member.getProfileImg())); - - response.sendRedirect("https://dkoqktaeu3tic.cloudfront.net/home"); + String targetUrl = UriComponentsBuilder.fromUriString("https://dkoqktaeu3tic.cloudfront.net/home") + .queryParam("accessToken", jwtToken.getAccessToken()) + .queryParam("refreshToken", jwtToken.getRefreshToken()) + .queryParam("memberId", String.valueOf(member.getId())) + .queryParam("profileImgUrl", member.getProfileImg()) + .build().toUriString(); + + getRedirectStrategy().sendRedirect(request, response, targetUrl); + + } private static String createCookie(String name, String value) { diff --git a/src/main/java/com/api/trip/common/sse/SseNotificationResponse.java b/src/main/java/com/api/trip/common/sse/SseNotificationResponse.java new file mode 100644 index 0000000..41503c5 --- /dev/null +++ b/src/main/java/com/api/trip/common/sse/SseNotificationResponse.java @@ -0,0 +1,15 @@ +package com.api.trip.common.sse; + +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.Getter; + +import java.util.List; + +@AllArgsConstructor +@Getter +public class SseNotificationResponse { + + private Long itemId; + private List tagNames; +} diff --git a/src/main/java/com/api/trip/domain/notification/emitter/SseEmitterMap.java b/src/main/java/com/api/trip/common/sse/emitter/SseEmitterMap.java similarity index 97% rename from src/main/java/com/api/trip/domain/notification/emitter/SseEmitterMap.java rename to src/main/java/com/api/trip/common/sse/emitter/SseEmitterMap.java index 26013c6..c45455d 100644 --- a/src/main/java/com/api/trip/domain/notification/emitter/SseEmitterMap.java +++ b/src/main/java/com/api/trip/common/sse/emitter/SseEmitterMap.java @@ -1,4 +1,4 @@ -package com.api.trip.domain.notification.emitter; +package com.api.trip.common.sse.emitter; import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Component; diff --git a/src/main/java/com/api/trip/domain/interesttag/respository/InterestTagRepositoryCustom.java b/src/main/java/com/api/trip/domain/interesttag/respository/InterestTagRepositoryCustom.java index 9cdb5ba..e756bb4 100644 --- a/src/main/java/com/api/trip/domain/interesttag/respository/InterestTagRepositoryCustom.java +++ b/src/main/java/com/api/trip/domain/interesttag/respository/InterestTagRepositoryCustom.java @@ -1,5 +1,6 @@ package com.api.trip.domain.interesttag.respository; +import com.api.trip.domain.interesttag.model.InterestTag; import com.api.trip.domain.member.model.Member; import java.util.List; @@ -7,4 +8,5 @@ public interface InterestTagRepositoryCustom { List findInterestTags(Member member); + List findInterestTagsByTagNames(List tagNames); } diff --git a/src/main/java/com/api/trip/domain/interesttag/respository/InterestTagRepositoryCustomImpl.java b/src/main/java/com/api/trip/domain/interesttag/respository/InterestTagRepositoryCustomImpl.java index e2e289a..3ff2ade 100644 --- a/src/main/java/com/api/trip/domain/interesttag/respository/InterestTagRepositoryCustomImpl.java +++ b/src/main/java/com/api/trip/domain/interesttag/respository/InterestTagRepositoryCustomImpl.java @@ -1,7 +1,9 @@ package com.api.trip.domain.interesttag.respository; +import com.api.trip.domain.interesttag.model.InterestTag; import com.api.trip.domain.interesttag.model.QInterestTag; import com.api.trip.domain.member.model.Member; +import com.api.trip.domain.tag.model.QTag; import com.querydsl.jpa.impl.JPAQueryFactory; import jakarta.persistence.EntityManager; @@ -25,4 +27,14 @@ public List findInterestTags(Member member) { .fetch(); } + @Override + public List findInterestTagsByTagNames(List tagNames) { + return jpaQueryFactory.select(interestTag) + .from(interestTag) + .innerJoin(interestTag.tag, QTag.tag).fetchJoin() + .where(interestTag.tag.name.in(tagNames)) + .fetch(); + + } + } diff --git a/src/main/java/com/api/trip/domain/interesttag/service/InterestTagService.java b/src/main/java/com/api/trip/domain/interesttag/service/InterestTagService.java index 67240d2..a6cbb85 100644 --- a/src/main/java/com/api/trip/domain/interesttag/service/InterestTagService.java +++ b/src/main/java/com/api/trip/domain/interesttag/service/InterestTagService.java @@ -43,4 +43,10 @@ public void createTag(Member member, List tagNames) { public List getInterestTag(Member member) { return interestTagRepository.findInterestTags(member); } + + @Transactional(readOnly = true) + public List getMemberByTags(List tagNames){ + List interestTags = interestTagRepository.findInterestTagsByTagNames(tagNames); + return interestTags.stream().map(InterestTag::getMember).toList(); + } } diff --git a/src/main/java/com/api/trip/domain/item/service/ItemService.java b/src/main/java/com/api/trip/domain/item/service/ItemService.java index 379bf10..fba6fb4 100644 --- a/src/main/java/com/api/trip/domain/item/service/ItemService.java +++ b/src/main/java/com/api/trip/domain/item/service/ItemService.java @@ -42,11 +42,11 @@ public Long createItemByDirect(CreateItemRequest itemRequest) { return itemRepository.save(item).getId(); } - public Long createItem(CreateItemRequest itemRequest){ + public Item createItem(CreateItemRequest itemRequest){ Item item = itemRequest.toEntity(); itemTagService.createItemTag(item, itemRequest.getTagNames()); + return itemRepository.save(item); - return itemRepository.save(item).getId(); } diff --git a/src/main/java/com/api/trip/domain/notification/controller/NotificationController.java b/src/main/java/com/api/trip/domain/notification/controller/NotificationController.java index cb17d73..343a1e3 100644 --- a/src/main/java/com/api/trip/domain/notification/controller/NotificationController.java +++ b/src/main/java/com/api/trip/domain/notification/controller/NotificationController.java @@ -4,7 +4,7 @@ import com.api.trip.common.exception.ErrorCode; import com.api.trip.domain.member.repository.MemberRepository; import com.api.trip.domain.notification.controller.dto.GetMyNotificationsResponse; -import com.api.trip.domain.notification.emitter.SseEmitterMap; +import com.api.trip.common.sse.emitter.SseEmitterMap; import com.api.trip.domain.notification.service.NotificationService; import lombok.RequiredArgsConstructor; import org.springframework.http.MediaType; @@ -31,7 +31,7 @@ public ResponseEntity connect() { .orElseThrow(() -> new CustomException(ErrorCode.UNAUTHORIZED)) .getId(); - SseEmitter sseEmitter = new SseEmitter(); + SseEmitter sseEmitter = new SseEmitter(3600000L); sseEmitterMap.put(memberId, sseEmitter); sseEmitterMap.send(memberId, "connect", LocalDateTime.now()); return ResponseEntity.ok(sseEmitter); diff --git a/src/main/java/com/api/trip/domain/notification/service/NotificationService.java b/src/main/java/com/api/trip/domain/notification/service/NotificationService.java index 6fb4a7d..cbaf280 100644 --- a/src/main/java/com/api/trip/domain/notification/service/NotificationService.java +++ b/src/main/java/com/api/trip/domain/notification/service/NotificationService.java @@ -2,6 +2,8 @@ import com.api.trip.common.exception.CustomException; import com.api.trip.common.exception.ErrorCode; +import com.api.trip.domain.interesttag.service.InterestTagService; +import com.api.trip.domain.item.model.Item; import com.api.trip.domain.itemtag.model.ItemTag; import com.api.trip.domain.member.model.Member; import com.api.trip.domain.member.repository.MemberRepository; @@ -21,6 +23,20 @@ public class NotificationService { private final MemberRepository memberRepository; private final NotificationRepository notificationRepository; + private final InterestTagService interestTagService; + + public void createNotification(Item item, List tagNames){ + + List receivers = interestTagService.getMemberByTags(tagNames); + + receivers.stream().forEach(member -> { + notificationRepository.save(Notification.builder() + .item(item) + .member(member).build()); + }); + + + } @Transactional(readOnly = true) public GetMyNotificationsResponse getMyNotifications(String email) {