Skip to content

Commit 2d05803

Browse files
committed
feat : 시즌 알림 기능 (#81)
1 parent 183f588 commit 2d05803

File tree

5 files changed

+80
-43
lines changed

5 files changed

+80
-43
lines changed

src/main/java/cmf/commitField/domain/noti/noti/controller/ApiV1NotiController.java

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
import cmf.commitField.global.error.ErrorCode;
88
import cmf.commitField.global.exception.CustomException;
99
import cmf.commitField.global.globalDto.GlobalResponse;
10+
import cmf.commitField.global.websocket.NotiWebSocketHandler;
1011
import lombok.RequiredArgsConstructor;
1112
import lombok.extern.slf4j.Slf4j;
1213
import org.springframework.security.core.Authentication;
@@ -28,6 +29,7 @@
2829
public class ApiV1NotiController {
2930
private final NotiService notiService;
3031
private final UserRepository userRepository;
32+
private final NotiWebSocketHandler notiWebSocketHandler;
3133

3234
@GetMapping("")
3335
public GlobalResponse<List<NotiDto>> getNoti() {
@@ -39,6 +41,9 @@ public GlobalResponse<List<NotiDto>> getNoti() {
3941
String username = (String) attributes.get("login"); // GitHub ID
4042
User user = userRepository.findByUsername(username).orElseThrow(() -> new CustomException(ErrorCode.NOT_FOUND_USER));
4143
List<NotiDto> notis = notiService.getNotReadNoti(user);
44+
// 웹소켓으로 알림 전송
45+
notiWebSocketHandler.sendNotification(user, notis);
46+
4247
return GlobalResponse.success(notis);
4348
}
4449

@@ -49,4 +54,19 @@ public GlobalResponse<List<NotiDto>> getNoti() {
4954
public void createNoti() {
5055

5156
}
52-
}
57+
58+
@PostMapping("/read")
59+
public GlobalResponse<Object> readNoti() {
60+
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
61+
62+
if (authentication instanceof OAuth2AuthenticationToken) {
63+
OAuth2User principal = (OAuth2User) authentication.getPrincipal();
64+
Map<String, Object> attributes = principal.getAttributes();
65+
String username = (String) attributes.get("login"); // GitHub ID
66+
User user = userRepository.findByUsername(username).orElseThrow(() -> new CustomException(ErrorCode.NOT_FOUND_USER));
67+
notiService.read(user);
68+
return GlobalResponse.success("알림을 읽음 처리했습니다.");
69+
}
70+
return GlobalResponse.error(ErrorCode.LOGIN_REQUIRED);
71+
}
72+
}

src/main/java/cmf/commitField/domain/noti/noti/repository/NotiRepository.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,5 +17,5 @@ public interface NotiRepository extends JpaRepository<Noti, Long> {
1717
@Query("SELECT new cmf.commitField.domain.noti.noti.dto.NotiDto(n.id, n.message, n.createdAt) " +
1818
"FROM Noti n JOIN n.receiver u WHERE u.id = :receiverId AND n.isRead = :isRead")
1919
Optional<List<NotiDto>> findNotiDtoByReceiverId(@Param("receiverId") Long receiverId, @Param("isRead") boolean isRead);
20-
21-
}
20+
Optional<List<Noti>> findNotiByReceiver(User receiver);
21+
}

src/main/java/cmf/commitField/domain/noti/noti/service/NotiService.java

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -88,4 +88,16 @@ public void createNewSeason(Season season) {
8888
});
8989
System.out.println("새 시즌 알림 생성 끝");
9090
}
91-
}
91+
92+
// 읽음 처리
93+
@Transactional
94+
public List<Noti> read(User receiver) {
95+
System.out.println("알림 읽음 처리");
96+
List<Noti> notis = notiRepository.findNotiByReceiver(receiver).orElseThrow(() -> new CustomException(ErrorCode.NOT_FOUND_USER));
97+
notis.forEach(noti -> {
98+
noti.setRead(true);
99+
});
100+
System.out.println("알림 읽음 처리 끝");
101+
return notis;
102+
}
103+
}

src/main/java/cmf/commitField/global/websocket/NotiWebSocketHandler.java

Lines changed: 44 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,53 +1,82 @@
11
package cmf.commitField.global.websocket;
22

3+
import cmf.commitField.domain.noti.noti.dto.NotiDto;
4+
import cmf.commitField.domain.noti.noti.entity.Noti;
5+
import cmf.commitField.domain.noti.noti.service.NotiService;
6+
import cmf.commitField.domain.user.entity.User;
7+
import com.fasterxml.jackson.databind.ObjectMapper;
8+
import lombok.RequiredArgsConstructor;
39
import lombok.extern.slf4j.Slf4j;
410
import org.springframework.stereotype.Component;
511
import org.springframework.web.socket.*;
612

713
import java.io.IOException;
8-
import java.util.ArrayList;
14+
import java.time.LocalDateTime;
15+
import java.util.HashMap;
916
import java.util.List;
17+
import java.util.Map;
18+
import java.util.concurrent.ConcurrentHashMap;
1019

1120
@Component
21+
@RequiredArgsConstructor
1222
@Slf4j
1323
public class NotiWebSocketHandler implements WebSocketHandler {
14-
15-
private final List<WebSocketSession> sessions = new ArrayList<>();
24+
private final NotiService notiService;
25+
private final ObjectMapper objectMapper;
26+
private final Map<Long, WebSocketSession> sessions = new ConcurrentHashMap<>();
1627

1728
@Override
1829
public void afterConnectionEstablished(WebSocketSession session) throws Exception {
19-
sessions.add(session);
20-
log.info("알림 WebSocket 연결됨: " + session);
30+
log.info("클라이언트 접속: {}", session.getId());
31+
32+
// 연결 성공 메시지 전송
33+
Map<String, Object> connectMessage = new HashMap<>();
34+
connectMessage.put("type", "SYSTEM");
35+
connectMessage.put("connect", "알림 서버에 연결되었습니다.");
36+
connectMessage.put("timestamp", LocalDateTime.now().toString());
37+
38+
try {
39+
session.sendMessage(new TextMessage(objectMapper.writeValueAsString(connectMessage)));
40+
} catch (Exception e) {
41+
log.error("연결 메시지 전송 실패: {}", e.getMessage());
42+
}
2143
}
2244

2345
@Override
2446
public void handleMessage(WebSocketSession session, WebSocketMessage<?> message) throws Exception {
25-
// 알림 메시지 처리 로직 (필요 시 구현)
47+
if (message instanceof TextMessage) {
48+
String payload = ((TextMessage) message).getPayload();
49+
log.info("Received message: {}", payload);
50+
} else {
51+
log.warn("Received unsupported message type: {}", message.getClass().getSimpleName());
52+
}
2653
}
2754

2855
@Override
2956
public void handleTransportError(WebSocketSession session, Throwable exception) throws Exception {
30-
log.error("알림 WebSocket 오류: " + exception.getMessage());
57+
log.error("WebSocket error: ", exception);
58+
session.close(CloseStatus.SERVER_ERROR);
3159
}
3260

3361
@Override
34-
public void afterConnectionClosed(WebSocketSession session, CloseStatus closeStatus) throws Exception {
35-
sessions.remove(session);
36-
log.info("알림 WebSocket 연결 종료됨: " + session);
62+
public void afterConnectionClosed(WebSocketSession session, CloseStatus status) throws Exception {
63+
sessions.values().remove(session);
64+
log.info("WebSocket disconnected: {}", status);
3765
}
3866

3967
@Override
4068
public boolean supportsPartialMessages() {
4169
return false;
4270
}
4371

44-
// 모든 유저에게 알림 메시지 전송
45-
public void sendNotificationToAllUsers(String message) {
46-
for (WebSocketSession session : sessions) {
72+
public void sendNotification(User receiver, List<NotiDto> noti) {
73+
WebSocketSession session = sessions.get(receiver.getId());
74+
if (session != null && session.isOpen()) {
4775
try {
48-
session.sendMessage(new TextMessage(message));
76+
String payload = objectMapper.writeValueAsString(noti);
77+
session.sendMessage(new TextMessage(payload));
4978
} catch (IOException e) {
50-
log.error("알림 메시지 전송 실패: " + e.getMessage());
79+
log.error("Failed to send WebSocket notification", e);
5180
}
5281
}
5382
}

src/main/java/cmf/commitField/global/websocket/NotiWebsoketConfig.java

Lines changed: 0 additions & 24 deletions
This file was deleted.

0 commit comments

Comments
 (0)