Skip to content

Commit

Permalink
Feature(#1) : 알림 응답 api 구현
Browse files Browse the repository at this point in the history
  • Loading branch information
Ban-gilhyeon committed Feb 4, 2025
1 parent ba74d01 commit 456d8f6
Show file tree
Hide file tree
Showing 8 changed files with 115 additions and 18 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,9 @@
@Getter
@RequiredArgsConstructor
public enum ExceptionMessage {
NOT_FOUND_USER("해당 유저를 찾을 수 없습니다.", HttpStatus.NOT_FOUND, "not found user");
NOT_FOUND_USER("해당 유저를 찾을 수 없습니다.", HttpStatus.NOT_FOUND, "not found user"),
NOT_FOUND_NOTIFICATION("해당 알림을 찾을 수 없습니다.", HttpStatus.NOT_FOUND, "not found notification"),
INVALID_NOTIFICATION_ID("잘못된 알림 ID 입니다.", HttpStatus.BAD_REQUEST, "invalid notification id");

private final String text;
private final HttpStatus status;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package urdego.io.urdego_notification_service.common.exception.notification;

import urdego.io.urdego_notification_service.common.exception.BaseException;
import urdego.io.urdego_notification_service.common.exception.ExceptionMessage;

public class InvalidNotificationId extends BaseException {
public static final BaseException EXCEPTION = new InvalidNotificationId();

private InvalidNotificationId() {
super(ExceptionMessage.INVALID_NOTIFICATION_ID);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package urdego.io.urdego_notification_service.common.exception.notification;

import urdego.io.urdego_notification_service.common.exception.BaseException;
import urdego.io.urdego_notification_service.common.exception.ExceptionMessage;

public class NotFoundNotification extends BaseException {
public static final BaseException EXCEPTION = new NotFoundNotification();

private NotFoundNotification() {
super(ExceptionMessage.NOT_FOUND_NOTIFICATION);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,30 +6,46 @@
import io.swagger.v3.oas.annotations.responses.ApiResponse;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.*;
import urdego.io.urdego_notification_service.common.exception.notification.NotFoundNotification;
import urdego.io.urdego_notification_service.controller.dto.request.NotificationRequest;
import urdego.io.urdego_notification_service.controller.dto.request.ReplyRequest;
import urdego.io.urdego_notification_service.controller.dto.response.NotificationResponse;
import urdego.io.urdego_notification_service.controller.dto.response.WebSocketMessageResponse;
import urdego.io.urdego_notification_service.domain.entity.Notification;
import urdego.io.urdego_notification_service.domain.service.NotificationService;

import java.util.Collections;
import java.util.List;
import java.util.UUID;
import java.util.stream.Collectors;
import java.util.stream.IntStream;

@Controller
@RequiredArgsConstructor
@RequestMapping("api/notification-service/notifications")
@Slf4j
public class NotificationController {
private final NotificationService notificationService;
private static final String PREFIX = "urdego_notification:";
private final RedisTemplate<String, Object> redisTemplate;

@PostMapping("/send")
@ApiResponse(responseCode = "200", content = @Content(schema = @Schema(implementation = WebSocketMessageResponse.class)))
@Operation(summary = "게임초대 알림 전송",description = "userId로 게임초대 알림 전송")
public ResponseEntity<WebSocketMessageResponse<Notification>> sendNotification(@RequestBody NotificationRequest request) {
WebSocketMessageResponse<Notification> response = notificationService.publishNotification(request);
return ResponseEntity.ok().body(response);
}

@GetMapping("/{userId}/reply")
@Operation(summary = "알림 답장", description = "notificationId로 게임초대 알림에 대한 답변 받기")
public ResponseEntity<Object> replyNotification(@PathVariable("userId") Long userId,
@RequestBody ReplyRequest request) {
Notification updatedNotification = notificationService.updateReadStatus(request, userId);
return ResponseEntity.ok().body(updatedNotification);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package urdego.io.urdego_notification_service.controller.dto.request;

import java.util.UUID;

public record ReplyRequest(
boolean isAccepted,
String notificationId
) {
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package urdego.io.urdego_notification_service.domain.entity;

import com.fasterxml.jackson.annotation.JsonFormat;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Getter;
Expand All @@ -18,8 +19,9 @@
@NoArgsConstructor
@Component
@Builder
@JsonIgnoreProperties(ignoreUnknown = true)
public class Notification implements Serializable {
private UUID NotificationId;
private UUID notificationId;

private Long senderId;
private Long targetId;
Expand All @@ -30,12 +32,18 @@ public class Notification implements Serializable {
private String targetNickname;
private Action action;

//수락여부
private boolean isAccepted;

//읽기 여부
private boolean isRead;

@JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd'T'HH:mm:ss")
private String timestamp;

public static Notification of(NotificationRequest request){
return Notification.builder()
.NotificationId(UUID.randomUUID())
.notificationId(UUID.randomUUID())
.roomId(request.roomId())
.roomName(request.roomName())
.senderId(request.senderId())
Expand All @@ -46,4 +54,10 @@ public static Notification of(NotificationRequest request){
.timestamp(LocalDateTime.now().toString())
.build();
}

public void updateReply(boolean isAccepted) {
this.isAccepted = isAccepted;
this.isRead = true;
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -2,22 +2,21 @@

import org.springframework.stereotype.Service;
import urdego.io.urdego_notification_service.controller.dto.request.NotificationRequest;
import urdego.io.urdego_notification_service.controller.dto.request.ReplyRequest;
import urdego.io.urdego_notification_service.controller.dto.response.NotificationResponse;
import urdego.io.urdego_notification_service.controller.dto.response.WebSocketMessageResponse;
import urdego.io.urdego_notification_service.domain.entity.Notification;

import java.util.List;
import java.util.UUID;

public interface NotificationService {

//메세지 발행
public WebSocketMessageResponse<Notification> publishNotification(NotificationRequest notificationRequest);
WebSocketMessageResponse<Notification> publishNotification(NotificationRequest notificationRequest);

//사용자 별 메세지 확인
List<Object> getUserNotifications(Long userId);

// 읽음 상태 저장
void updateReadStatus(Long userId, String lastReadMessageId);
// 답장 및 읽음 상태 변경
Notification updateReadStatus(ReplyRequest request, Long userId);

//읽음 상태 조회
String getLastReadMessage(String userId);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,22 +5,30 @@
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.messaging.simp.SimpMessagingTemplate;
import org.springframework.stereotype.Service;
import urdego.io.urdego_notification_service.common.exception.notification.InvalidNotificationId;
import urdego.io.urdego_notification_service.common.exception.notification.NotFoundNotification;
import urdego.io.urdego_notification_service.controller.client.GameServiceClient;
import urdego.io.urdego_notification_service.controller.dto.request.NotificationRequest;
import urdego.io.urdego_notification_service.controller.dto.request.ReplyRequest;
import urdego.io.urdego_notification_service.controller.dto.response.NotificationResponse;
import urdego.io.urdego_notification_service.controller.dto.response.WebSocketMessageResponse;
import urdego.io.urdego_notification_service.domain.entity.Notification;

import java.util.List;
import java.util.UUID;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;
import java.util.stream.IntStream;

@RequiredArgsConstructor
@Service
@Slf4j
public class NotificationServiceImpl implements NotificationService {
private final SimpMessagingTemplate simpMessagingTemplate;
private final RedisTemplate<String, Object> redisTemplate;
private final GameServiceClient gameServiceClient;
private static final String PREFIX = "urdego_notification:";
private static final long EXPIRATION_TIME = 3;
private static final long EXPIRATION_TIME = 2; //2일

@Override
public WebSocketMessageResponse<Notification> publishNotification(NotificationRequest request) {
Expand All @@ -37,15 +45,26 @@ public WebSocketMessageResponse<Notification> publishNotification(NotificationRe
}

@Override
public List<Object> getUserNotifications(Long userId) {
return redisTemplate.opsForList().range(PREFIX + userId, 0, -1);
}
public Notification updateReadStatus(ReplyRequest request, Long userId) {
// 키 생성
String key = PREFIX + userId;

List<Notification> notifications = readNotificationList(userId);

@Override
public void updateReadStatus(Long userId, String lastReadMessageId) {
//notificationId의 알림 index 찾기 없으면 Exception!!
int index = IntStream.range(0, notifications.size())
.filter(i -> notifications.get(i).getNotificationId().toString().equals(request.notificationId()))
.findFirst().orElseThrow(() -> InvalidNotificationId.EXCEPTION);

Notification updatedNotification = notifications.get(index);
updatedNotification.updateReply(request.isAccepted());

//redis에 수정사항 저장
redisTemplate.opsForList().set(key,index, updatedNotification);
return updatedNotification;
}


@Override
public String getLastReadMessage(String userId) {
return "";
Expand All @@ -57,4 +76,18 @@ public void saveNotification(Notification notification) {
redisTemplate.opsForList().rightPush(key, notification);
redisTemplate.expire(key,EXPIRATION_TIME, TimeUnit.DAYS);
}

//키에 대한 벨류 조회
private List<Notification> readNotificationList(Long userId) {
// 키 생성
String key = PREFIX + userId;

List<Object> rawNotification = redisTemplate.opsForList().range(key, 0, -1);
if(rawNotification == null || rawNotification.size() == 0) { throw NotFoundNotification.EXCEPTION;}
//Object -> Notification
List<Notification> notifications = rawNotification.stream().filter(obj -> obj instanceof Notification)
.map(obj -> (Notification) obj).collect(Collectors.toList());

return notifications;
}
}

0 comments on commit 456d8f6

Please sign in to comment.