Skip to content
Open
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 @@ -70,11 +70,15 @@ public class SecurityConfig {
new AntPathRequestMatcher("/custom-logout"),
new AntPathRequestMatcher("/movielog/**"),
new AntPathRequestMatcher("/movie-search/**"),
new AntPathRequestMatcher("/comment/read/**")
new AntPathRequestMatcher("/comment/read/**"),
new AntPathRequestMatcher("/passwordfind/**"),
new AntPathRequestMatcher("/notifications/**")



);


);


//HttpSecurity 객체를 사용하여 Spring Security의 인증 및 권한 부여 규칙을 정의
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,10 @@


import com.ItsTime.ItNovation.domain.comment.dto.CommentWriteRequestDto;
import com.ItsTime.ItNovation.domain.review.ReviewRepository;
import com.ItsTime.ItNovation.domain.user.User;
import com.ItsTime.ItNovation.domain.user.UserRepository;
import com.ItsTime.ItNovation.notify.NotificationService;
import com.ItsTime.ItNovation.service.comment.CommentService;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
Expand All @@ -15,17 +19,33 @@
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

import java.util.Optional;

@RestController
@RequestMapping("/comment")
@RequiredArgsConstructor
@Slf4j
public class CommentController {

private final NotificationService notificationService;
private final UserRepository userRepository;
private final ReviewRepository reviewRepository;

private final CommentService commentService;
@PostMapping("/write")
public ResponseEntity commentWrite(@RequestBody CommentWriteRequestDto commentDto, Authentication authentication){
//리뷰댓글다는 사람(pub)
String email = authentication.getName();
Optional<User> pubuser = userRepository.findByEmail(email);
if(pubuser.isPresent()){
Long pubId=pubuser.get().getId();
//리뷰주인- 알람받는사람(sub)
Long reviewId = commentDto.getReviewId();
Long subId=reviewRepository.findById(reviewId).get().getUser().getId();
//subId에게 리뷰댓글다는 사람의 id 를 알려주고 이벤트 카테고리는 리뷰댓글
//프론트엔드가 이제 subId로 sse 연결을 맺고 pubId님이 댓글을 달았다고 공지 주면됨
notificationService.notify(subId,pubId,"comment");
}

return commentService.writeComment(commentDto, email);
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
package com.ItsTime.ItNovation.notify;

import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Repository;
import org.springframework.web.servlet.mvc.method.annotation.SseEmitter;

import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;

@Repository
@RequiredArgsConstructor
public class EmitterRepository {
private final Map<Long, SseEmitter> emitters = new ConcurrentHashMap<>();
/**
* 주어진 아이디와 이미터를 저장
*
* @param id - 사용자 아이디.
* @param emitter - 이벤트 Emitter.
*/
public void save(Long id, SseEmitter emitter) {
emitters.put(id, emitter);
}
/**
* 주어진 아이디의 Emitter를 제거
*
* @param id - 사용자 아이디.
*/
public void deleteById(Long id) {
emitters.remove(id);
}
/**
* 주어진 아이디의 Emitter를 가져옴.
*
* @param id - 사용자 아이디.
* @return SseEmitter - 이벤트 Emitter.
*/
public SseEmitter get(Long id) {
return emitters.get(id);
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
package com.ItsTime.ItNovation.notify;

import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.servlet.mvc.method.annotation.SseEmitter;


@RestController
@RequestMapping("/notifications")
@RequiredArgsConstructor
@Slf4j
public class NotificationController {

private final NotificationService notificationService;

@GetMapping(value = "/subscribe/{userId}", produces = MediaType.TEXT_EVENT_STREAM_VALUE)
public SseEmitter subscribe(@PathVariable Long userId) {
return notificationService.subscribe(userId,"comment");
}

@PostMapping("/send-data/{userId}")
public void sendData(@PathVariable Long userId) {
notificationService.notify(userId, "data","comment");
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
package com.ItsTime.ItNovation.notify;

import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
import org.springframework.web.servlet.mvc.method.annotation.SseEmitter;

import java.io.IOException;

@Service
@RequiredArgsConstructor
public class NotificationService {
private static final Long DEFAULT_TIMEOUT=60L*1000*60;
private final EmitterRepository emitterRepository;
/**
* 클라이언트가 구독을 위해 호출하는 메서드.
*
* @param userId - 구독하는 클라이언트의 사용자 아이디.
* @return SseEmitter - 서버에서 보낸 이벤트 Emitter
* @param eventCategory - 이벤트이름
*/
public SseEmitter subscribe(Long userId,String eventCategory) {
SseEmitter emitter = createEmitter(userId);
sendToClient(userId, "EventSteam created. [ userId=" + userId + "]",eventCategory);
return emitter;
}
/**
* 클라이언트에게 데이터를 전송
*
* @param userId - 데이터를 받을 사용자의 아이디.
* @param data - 전송할 데이터.
* @param eventCategory - 이벤트이름
*/
private void sendToClient(Long userId, Object data,String eventCategory) {
eventCategory = eventCategory != null ? eventCategory : "notify";
SseEmitter emitter = emitterRepository.get(userId);
if (emitter != null) {
try{
emitter.send(SseEmitter.event().id(String.valueOf(userId)).name(eventCategory).data(data));
} catch (IOException e) {
emitterRepository.deleteById(userId);
emitter.completeWithError(e);
}
}
}
/**
* 사용자 아이디를 기반으로 이벤트 Emitter를 생성
*
* @param userId - 사용자 아이디.
* @return SseEmitter - 생성된 이벤트 Emitter.
*/
private SseEmitter createEmitter(Long userId) {
SseEmitter emitter = new SseEmitter(DEFAULT_TIMEOUT);
emitterRepository.save(userId,emitter);
emitter.onCompletion(() -> emitterRepository.deleteById(userId));
emitter.onTimeout(() -> emitterRepository.deleteById(userId));
return emitter;
}
/**
* 서버의 이벤트를 클라이언트에게 보내는 메서드
* 다른 서비스 로직에서 이 메서드를 사용해 데이터를 Object event에 넣고 전송하면 된다.
*
* @param userId - 메세지를 전송할 사용자의 아이디.
* @param event - 전송할 이벤트 객체.
* @param eventCategory - 이벤트이름
*/
public void notify(Long userId, Object event,String eventCategory) {
sendToClient(userId, event,eventCategory);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@
import com.ItsTime.ItNovation.domain.user.User;
import com.ItsTime.ItNovation.domain.user.UserRepository;
import java.util.Optional;

import com.ItsTime.ItNovation.notify.NotificationService;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.ResponseEntity;
Expand All @@ -34,13 +36,15 @@ public class PushService {
private final FollowRepository followRepository;
private final MovieRepository movieRepository;
private final MovieLikeRepository movieLikeRepository;
private final NotificationService notificationService;

@Transactional
public ResponseEntity pushReviewLike(ReviewLikeRequestDto reviewLikeRequestDto, User findUser) {
try {
log.info(reviewLikeRequestDto.getReviewId().toString());
Review review = reviewRepository.findById(reviewLikeRequestDto.getReviewId())
.orElseThrow(() -> new IllegalArgumentException("해당 리뷰가 없어요!"));

Optional<ReviewLike> reviewLike = reviewLikeRepository.findReviewLikeByReviewIdAndUserId(
findUser.getId(), reviewLikeRequestDto.getReviewId());
return getPushReviewLikeResponseDtoResponseEntity(review, findUser, reviewLike);
Expand All @@ -58,14 +62,17 @@ private ResponseEntity<PushReviewLikeResponseDto> getPushReviewLikeResponseDtoRe
reviewLikeRepository.save(build);
PushReviewLikeResponseDto reviewLikeDto = buildPushReviewLikeResponseDto(
build);
notificationService.notify(review.getUser().getId(), user.getId(), "reviewLike");

return ResponseEntity.status(200).body(reviewLikeDto);
}
else{
ReviewLike presentReviewLike = reviewLike.get();
presentReviewLike.updateReviewLike();

//reviewLikeRepository.save(presentReviewLike); //transactional 작성 꼭 해야함 안 하면 더티 체킹 안함.

if (presentReviewLike.getReviewLike() == true) {
notificationService.notify(review.getUser().getId(), user.getId(), "reviewLike");
}
PushReviewLikeResponseDto reviewLikeDto = buildPushReviewLikeResponseDto(
presentReviewLike);
return ResponseEntity.status(200).body(reviewLikeDto);
Expand Down Expand Up @@ -113,13 +120,16 @@ private ResponseEntity<FollowStateResponseDto> getFollowStateResponseDtoResponse
if(findByPushUserAndFollowUser.isEmpty()){
FollowState build = getFollower(pushUser, targetUser);
followRepository.save(build);

FollowStateResponseDto followStateResponseDto = FollowStateResponseDto.builder()
.isFollow(true).build();
notificationService.notify(targetUser.getId(), pushUser.getId(), "follow");
return ResponseEntity.status(200).body(followStateResponseDto);
}
else{
FollowState find = findByPushUserAndFollowUser.get();
followRepository.delete(find);

FollowStateResponseDto followStateResponseDto = FollowStateResponseDto.builder()
.isFollow(false).build();
return ResponseEntity.status(200).body(followStateResponseDto);
Expand Down Expand Up @@ -160,6 +170,7 @@ private ResponseEntity getMovieLikeStateResponseDtoResponseEntity(User movieLike
if(findByUserAndMovie.isEmpty()){
MovieLike build = getMovieLike(movieLikeUser, movie);
movieLikeRepository.save(build);

MovieLikeStateResponseDto movieLikeStateResponseDto = MovieLikeStateResponseDto.builder()
.isMovieLike(true).build();
return ResponseEntity.status(200).body(movieLikeStateResponseDto);
Expand Down