Skip to content

Commit

Permalink
Merge pull request TeamMajorLink#52 from kchaeeun/feat#38
Browse files Browse the repository at this point in the history
  • Loading branch information
kchaeeun authored Aug 19, 2024
2 parents 803af01 + d98da0d commit 2e70dc3
Show file tree
Hide file tree
Showing 14 changed files with 359 additions and 8 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
package com.example.majorLink.controller;

import com.example.majorLink.domain.User;
import com.example.majorLink.dto.response.NotificationResponse;
import com.example.majorLink.global.auth.AuthUser;
import com.example.majorLink.service.NotificationService;
import lombok.RequiredArgsConstructor;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.security.core.annotation.AuthenticationPrincipal;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.servlet.mvc.method.annotation.SseEmitter;

import java.util.List;

@RestController
@RequestMapping("/notification")
@RequiredArgsConstructor
public class NotificationController {
private final NotificationService notificationService;

// Last-Event-ID는 SSE 연결이 끊어졌을 경우, 클라이언트가 수신한 마지막 데이터 ID 값을 의미, 항상 존재 X -> false

/**
* 알림을 위한 구독 API
* [GET] /notification/subscribe
* @param authUser
* @param lastEventId
* @return
*/
@GetMapping(value = "/subscribe", produces = "text/event-stream")
public SseEmitter subscribe(
@AuthenticationPrincipal AuthUser authUser,
@RequestHeader(value = "Last-Event-ID", required = false, defaultValue = "") String lastEventId) {
User user = authUser.getUser();
return notificationService.subscribe(user, lastEventId);
}

/**
* 알림 전체 조회
* [GET] /notification
* @param authUser
* @return
*/
@GetMapping
public ResponseEntity<List<NotificationResponse>> getNotificationList(
@AuthenticationPrincipal AuthUser authUser) {
User user = authUser.getUser();

List<NotificationResponse> notificationResponse = notificationService.getNotificationList(user);

return ResponseEntity.status(HttpStatus.OK).body(notificationResponse);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@
import com.example.majorLink.domain.enums.Level;
import jakarta.persistence.*;
import lombok.*;
import org.hibernate.annotations.OnDelete;
import org.hibernate.annotations.OnDeleteAction;

import java.time.LocalTime;
import java.util.Date;
Expand Down Expand Up @@ -67,6 +69,11 @@ public class Lecture extends BaseEntity{
@Column(nullable = false, length = 100)
private String tutor;

@OnDelete(action = OnDeleteAction.CASCADE)
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "user_id")
private User user;

public void updateLecture(String name, String body, int curri, String info, Level level, int pNum, LocalTime time, Day day, Date startDate, Exam exam, Category category, String tag ){
this.name = name;
this.body = body;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@
import com.example.majorLink.domain.mapping.UserNotification;
import jakarta.persistence.*;
import lombok.*;
import org.hibernate.annotations.OnDelete;
import org.hibernate.annotations.OnDeleteAction;

import java.util.ArrayList;
import java.util.List;
Expand All @@ -19,14 +21,25 @@ public class Notification extends BaseEntity{
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;

@Column(nullable = false, length = 100)
private String title;

@Column(nullable = false, length = 1000)
private String content;

@Enumerated(EnumType.STRING)
@Column(columnDefinition = "VARCHAR(10) DEFAULT 'UNCHECK'")
private CheckStatus status;
@Column(nullable = false)
private String url;

// receiver 삭제 시 연관관계 동시 삭제
@OnDelete(action = OnDeleteAction.CASCADE)
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "receiver_id")
private User receiver;

@OnDelete(action = OnDeleteAction.CASCADE)
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "sender_id")
private User sender;

@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "lecture_id")
private Lecture lecture;

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package com.example.majorLink.domain;

import jakarta.persistence.Column;
import jakarta.persistence.Embeddable;
import jakarta.persistence.Embedded;
import lombok.Getter;
import lombok.NoArgsConstructor;

@Getter
@Embeddable
@NoArgsConstructor
public class NotificationContent {
@Column(nullable = false)
private String content;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
package com.example.majorLink.domain;

import jakarta.persistence.Column;
import jakarta.persistence.Embeddable;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;

@Getter
@Setter
@Embeddable
@NoArgsConstructor
public class RelatedUrl {
@Column(nullable = false)
private String url;
}
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ public class User extends BaseEntity{
@Column(length = 40)
private String favorite;

@Column(name = "learnPart", nullable = false, columnDefinition = "VARCHAR(20)")
@Column(name = "learnPart", columnDefinition = "VARCHAR(20)")
private String learnPart;

@Column(nullable = false, columnDefinition = "INT DEFAULT 0")
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
package com.example.majorLink.domain.enums;

public enum NotificationType {
APPLICATION
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
package com.example.majorLink.dto.response;

import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Getter;
import lombok.NoArgsConstructor;

@Getter
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class NotificationResponse {
private Long id;
private String receiver;
private String sender;
private String content;
private String url;
private String createdAt;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
package com.example.majorLink.repository;

import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.web.servlet.mvc.method.annotation.SseEmitter;

import java.util.Map;
import java.util.UUID;

public interface EmitterRepository {
SseEmitter save(String emitterId, SseEmitter sseEmitter);
void saveEventCache(String emitterId, Object event);
// 해당 유저와 관련된 모든 emitter를 찾는다
Map<String, SseEmitter> findAllEmitterStartWithByUserId(UUID userId);
// 해당 유저와 관련된 모든 이벤트를 찾는다.
Map<String, Object> findAllEventCacheStartWithByUserId(UUID userId);

void deleteById(String emitterId);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
package com.example.majorLink.repository;

import org.springframework.web.servlet.mvc.method.annotation.SseEmitter;

import java.util.Map;
import java.util.Objects;
import java.util.UUID;
import java.util.concurrent.ConcurrentHashMap;
import java.util.stream.Collectors;

public class EmitterRepositoryImpl implements EmitterRepository{
// 동시성 고려를 위해 ConcurrentHashMap 사용
private final Map<String, SseEmitter> emitters = new ConcurrentHashMap<>();
private final Map<String, Object> eventCache = new ConcurrentHashMap<>();

@Override
public SseEmitter save(String emitterId, SseEmitter sseEmitter) {
emitters.put(emitterId, sseEmitter);
return sseEmitter;
}

@Override
public void saveEventCache(String emitterId, Object event) {
eventCache.put(emitterId, event);
}

@Override
public Map<String, SseEmitter> findAllEmitterStartWithByUserId(UUID userId) {
return emitters.entrySet().stream()
.filter(entry -> entry.getKey().startsWith(String.valueOf(userId)))
.collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue));
}

@Override
public Map<String, Object> findAllEventCacheStartWithByUserId(UUID userId) {
return eventCache.entrySet().stream()
.filter(entry -> entry.getKey().startsWith(String.valueOf(userId)))
.collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue));
}

@Override
public void deleteById(String emitterId) {
emitters.remove(emitterId);
}


}
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package com.example.majorLink.repository;

import com.example.majorLink.domain.Notification;
import com.example.majorLink.domain.User;
import com.example.majorLink.dto.response.NotificationResponse;
import org.springframework.data.jpa.repository.JpaRepository;

import java.util.List;

public interface NotificationRepository extends JpaRepository<Notification, Long> {

List<Notification> findAllByReceiver(User user);
}
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ public class LectureServiceImpl implements LectureService {
private final TutorLectureRepository tutorLectureRepository;
private final TuteeLectureRepository tuteeLectureRepository;
private final LikedRepository likedRepository;
private final NotificationService notificationService;

// 강의 생성
@Override
Expand All @@ -56,6 +57,7 @@ public Lecture createLecture(UUID userId, LectureRequestDTO request) {
.tag(request.getTag())
.tutor(user.getNickname())
.cNum(0)
.user(user)
.build();

Lecture saveLecture = lectureRepository.save(lecture);
Expand Down Expand Up @@ -174,7 +176,14 @@ public TuteeLecture addLecture(UUID userId, Long lectureId) {

lecture.addCurPNum();

return tuteeLectureRepository.save(tuteeLecture);
TuteeLecture savedTuteeLecture = tuteeLectureRepository.save(tuteeLecture);

// 수강 신청 시 튜터에게 알림 전달
String msg = user.getNickname() + " 님으로 부터 수업 신청이 왔습니다.";
notificationService.send(user, lecture, msg);

return savedTuteeLecture;

}

// 강의 좋아요
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package com.example.majorLink.service;

import com.example.majorLink.domain.*;
import com.example.majorLink.dto.response.NotificationResponse;
import org.springframework.web.servlet.mvc.method.annotation.SseEmitter;

import java.util.List;

public interface NotificationService {
SseEmitter subscribe(User user, String lastEventId);
void send(User sender, Lecture lecture, String content);

// 전체 알림 조회
List<NotificationResponse> getNotificationList(User user);
}
Loading

0 comments on commit 2e70dc3

Please sign in to comment.