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
4 changes: 4 additions & 0 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,10 @@ dependencies {
//Websocket
implementation 'org.springframework.boot:spring-boot-starter-websocket'

// rabbitmq
implementation 'com.rabbitmq:amqp-client:5.12.0'
implementation 'org.springframework.boot:spring-boot-starter-amqp'

//Test
testImplementation 'org.springframework.boot:spring-boot-starter-test'
testImplementation 'org.springframework.amqp:spring-rabbit-test'
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,140 @@
package com.aliens.friendship.backend_chatting_server.chatting.business;

import com.aliens.friendship.backend_chatting_server.chatting.dto.response.ChatSendResponse;
import com.aliens.friendship.backend_chatting_server.chatting.dto.response.ChatSummariesResponse;
import com.aliens.friendship.backend_chatting_server.chatting.service.ChatService;
import com.aliens.friendship.backend_chatting_server.db.chat.entity.ChatEntity;
import com.aliens.friendship.backend_chatting_server.fcm.service.FcmService;
import com.aliens.friendship.backend_chatting_server.global.common.Business;
import com.aliens.friendship.backend_chatting_server.global.util.jwt.JwtTokenUtil;
import com.aliens.friendship.backend_chatting_server.websocket.converter.WebsocketConverter;
import com.aliens.friendship.backend_chatting_server.websocket.dto.request.BulkReadRequest;
import com.aliens.friendship.backend_chatting_server.websocket.dto.request.ChatSendRequest;
import com.aliens.friendship.backend_chatting_server.websocket.dto.request.SingleReadRequest;
import com.aliens.friendship.backend_chatting_server.websocket.dto.response.BulkReadWebsocketResponse;
import com.aliens.friendship.backend_chatting_server.websocket.dto.response.ChatSendWebsocketResponse;
import com.aliens.friendship.backend_chatting_server.websocket.dto.response.SingleReadWebsocketResponse;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.google.firebase.messaging.FirebaseMessagingException;
import lombok.RequiredArgsConstructor;
import org.springframework.web.socket.TextMessage;
import org.springframework.web.socket.WebSocketSession;

import java.io.IOException;
import java.util.List;

@Business
@RequiredArgsConstructor
public class ChatBusiness {
private final ChatService chatService;
private final JwtTokenUtil jwtTokenUtil;
private final ObjectMapper objectMapper;
private final FcmService fcmService;
private final WebsocketConverter websocketConverter;

public void sendChat(WebSocketSession session, TextMessage message, List<Long> roomIds) throws FirebaseMessagingException, IOException {
// 요청 역직렬화
ChatSendRequest chatSendRequest = objectMapper.readValue(message.getPayload(), ChatSendRequest.class);

// roomId 검증
chatService.validateRoomId(chatSendRequest.getRoomId(), roomIds);

// 채팅 저장
ChatSendResponse chatSendResponse = chatService.saveChat(chatSendRequest);

// 상대방에게 새로운 채팅 FCM 전송
fcmService.sendSingleChatToReceiverByToken(chatSendResponse);

// 채팅 전송 요청에 대한 응답 생성
ChatSendWebsocketResponse chatWebSocketResponse = websocketConverter.toChatSendWebsocketResponseWithRequest(chatSendRequest, chatSendResponse);

// 응답 직렬화
String response = objectMapper.writeValueAsString(chatWebSocketResponse);

// response를 WebSocketSession에 전송
session.sendMessage(new TextMessage(response));
}

public void updateSingleRead(WebSocketSession session, TextMessage message, List<Long> roomIds) throws FirebaseMessagingException, IOException {
// 요청 역직렬화
SingleReadRequest singleReadRequest = objectMapper.readValue(message.getPayload(), SingleReadRequest.class);

// roomId 검증
chatService.validateRoomId(singleReadRequest.getRoomId(), roomIds);

// chatId로 chatEntity 조회
ChatEntity chatEntity = chatService.getChatEntity(singleReadRequest.getChatId());

// 단일 메시지 읽음 상태 업데이트
chatService.updateReadStateByChatId(chatEntity);

// 상대방에게 읽음 처리 FCM 전송
fcmService.sendSingleChatReadToSenderByToken(chatEntity);

// 읽기 처리 요청에 대한 응답 생성
SingleReadWebsocketResponse singleReadWebsocketResponse = websocketConverter.toSingleReadWebsocketResponseWithRequest(singleReadRequest);

// 응답 직렬화
String response = objectMapper.writeValueAsString(singleReadWebsocketResponse);

// response를 WebSocketSession에 전송
session.sendMessage(new TextMessage(response));
}

public void updateBulkRead(WebSocketSession session, TextMessage message, List<Long> roomIds) throws FirebaseMessagingException, IOException {
// 요청 역직렬화
BulkReadRequest bulkReadRequest = objectMapper.readValue(message.getPayload(), BulkReadRequest.class);

// roomId 검증
chatService.validateRoomId(bulkReadRequest.getRoomId(), roomIds);

// 채팅방기준 일괄 읽음 상태 업데이트
Long chatId = chatService.updateReadStateByRoomId(bulkReadRequest.getRoomId(), bulkReadRequest.getPartnerId());

// 상대방에게 채팅방 기준 일괄 읽음 처리 FCM 전송
fcmService.sendBulkChatReadToSenderByToken(bulkReadRequest);

// 일괄 처리 요청에 대한 웹소켓 응답 생성
BulkReadWebsocketResponse readByRoomWebsocketResponse = websocketConverter.toBulkReadWebsocketResponseWithRequest(bulkReadRequest);

// 응답 직렬화
String response = objectMapper.writeValueAsString(readByRoomWebsocketResponse);

// response를 WebSocketSession에 전송
session.sendMessage(new TextMessage(response));
}

public List<Long> afterWebsocketConnectionEstablished(WebSocketSession session) {
// header JWT 토큰 가져오기
String JWTToken = session.getHandshakeHeaders().get("Authorization").get(0);

// 토큰 검증
jwtTokenUtil.validateToken(JWTToken);

// header token에서 roomId 추출후 반환
return jwtTokenUtil.getRoomIdsFromToken(JWTToken);
}

public List<ChatSendResponse> getUnreadChatsByRoomId(String jwtToken, Long roomId) throws JsonProcessingException {
// 유효 roomId 추출
List<Long> roomIdsFromJwt = jwtTokenUtil.getRoomIdsFromToken(jwtToken);

// roomId 검증
chatService.validateRoomId(roomId, roomIdsFromJwt);

// 읽지 않은 채팅 목록 반환
return chatService.getUnreadChatsByRoomId(roomId);
}

public ChatSummariesResponse getSummaryChats(String jwtToken) {
// 유효 roomId 추출
List<Long> roomIdsFromJwt = jwtTokenUtil.getRoomIdsFromToken(jwtToken);

// 요청한 멤버의 memberId 추출
Long memberId = jwtTokenUtil.getCurrentMemberIdFromToken(jwtToken);

// 채팅 요약 정보 반환
return chatService.getChatSummaries(roomIdsFromJwt, memberId);
}
}
Original file line number Diff line number Diff line change
@@ -1,65 +1,31 @@
package com.aliens.friendship.backend_chatting_server.chatting.controller;

import com.aliens.friendship.backend_chatting_server.chatting.dto.ChatRequestDto;
import com.aliens.friendship.backend_chatting_server.chatting.dto.ChatResponseDto;
import com.aliens.friendship.backend_chatting_server.chatting.dto.NewChatWithCountOfUnreadChats;
import com.aliens.friendship.backend_chatting_server.fcm.service.FCMService;
import com.aliens.friendship.backend_chatting_server.jwt.JwtTokenProvider;
import com.aliens.friendship.backend_chatting_server.chatting.business.ChatBusiness;
import com.aliens.friendship.backend_chatting_server.chatting.dto.response.ChatSendResponse;
import com.aliens.friendship.backend_chatting_server.chatting.dto.response.ChatSummariesResponse;
import com.aliens.friendship.backend_chatting_server.fcm.service.FcmService;
import com.aliens.friendship.backend_chatting_server.global.util.jwt.JwtTokenUtil;
import com.aliens.friendship.backend_chatting_server.chatting.service.ChatService;
import com.fasterxml.jackson.core.JsonProcessingException;
import lombok.RequiredArgsConstructor;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.messaging.handler.annotation.MessageMapping;
import org.springframework.messaging.simp.SimpMessageSendingOperations;
import org.springframework.web.bind.annotation.*;

import javax.validation.Valid;
import java.util.List;

@RestController
@RequestMapping("/api/v1")
@RequiredArgsConstructor
public class ChatController {
private final ChatBusiness chatBusiness;

private final SimpMessageSendingOperations msgTemplate;
private final ChatService chatService;
private final JwtTokenProvider jwtTokenProvider;
private final FCMService FCMService;

//채팅 전송 및 저장
@MessageMapping("/chat/msg")
public void sendMessage(@RequestHeader("ChattingToken") String jwtToken, @RequestBody @Valid ChatRequestDto chatRequestDto) {
jwtTokenProvider.validateToken(jwtToken);
FCMService.sendSingleChatToReceiverByToken(chatService.addChat(chatRequestDto));
}

@MessageMapping("/chat/msg/{chatId}")
public void changeReadStatus(@RequestHeader("ChattingToken") String jwtToken, @PathVariable Long chatId) {
jwtTokenProvider.validateToken(jwtToken);
FCMService.sendReadChatToSenderByToken(chatService.changeChatToReadByChatId(chatId));
}

// 현재 사용자의 jwt 기반으로 룸들의 새로운 채팅과 안읽은 채팅개수를 반환시켜준다 .
@GetMapping("/chat")
public ResponseEntity<List<NewChatWithCountOfUnreadChats>> getNewChatAndCountOfUnreadChats(@RequestHeader("ChattingToken") String jwtToken) {
jwtTokenProvider.validateToken(jwtToken);
List<Long> roomIdsFromToken = jwtTokenProvider.getRoomIdsFromToken(jwtToken);
Long currentMemberId = jwtTokenProvider.getCurrentMemberIdFromToken(jwtToken);
return new ResponseEntity<>(chatService.getNewChatAndNotReadCountOfChatInEachRoomsByRoomIds(currentMemberId, roomIdsFromToken), HttpStatus.OK);
}

//현재 사용자의 jwt 안에 RoomId와 현재 얻고자하는 roomId를 비교하여
// 접근이 가능하다면 해당 채팅방의 100개 채팅내역을 가져온다.
@GetMapping("/chat/{roomId}")
public ResponseEntity<List<ChatResponseDto>> getHundredChatsByRoomId(@RequestHeader("ChattingToken") String jwtToken, @PathVariable Long roomId) {
jwtTokenProvider.validateToken(jwtToken);
List<Long> roomIdsFromToken = jwtTokenProvider.getRoomIdsFromToken(jwtToken);

boolean validRoomId = roomIdsFromToken.contains(roomId);

return validRoomId
? ResponseEntity.ok(chatService.getHundredChatsByRoomId(roomId))
: ResponseEntity.badRequest().build();
public ResponseEntity<List<ChatSendResponse>> getUnreadChatsByRoomId(@RequestHeader("ChattingToken") String jwtToken, @PathVariable Long roomId) throws JsonProcessingException {
return ResponseEntity.ok(chatBusiness.getUnreadChatsByRoomId(jwtToken, roomId));
}

@GetMapping("/chat/summary")
public ResponseEntity<ChatSummariesResponse> getSummaryChatsByRoomId(@RequestHeader("ChattingToken") String jwtToken) {
return ResponseEntity.ok(chatBusiness.getSummaryChats(jwtToken));
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
package com.aliens.friendship.backend_chatting_server.chatting.converter;

import com.aliens.friendship.backend_chatting_server.chatting.dto.response.ChatSendResponse;
import com.aliens.friendship.backend_chatting_server.db.chat.entity.ChatEntity;
import com.aliens.friendship.backend_chatting_server.db.chat.entity.ChatType;
import com.aliens.friendship.backend_chatting_server.global.common.Converter;
import com.aliens.friendship.backend_chatting_server.websocket.dto.request.ChatSendRequest;

@Converter
public class ChatConverter {

public ChatEntity toChatEntityWithRequest(ChatSendRequest chatSendRequest) {
return ChatEntity.builder()
.roomId(chatSendRequest.getRoomId())
.senderId(chatSendRequest.getSenderId())
.senderName(chatSendRequest.getSenderName())
.receiverId(chatSendRequest.getReceiverId())
.chatType(ChatType.valueOf(chatSendRequest.getChatType()))
.chatContent(chatSendRequest.getChatContent())
.sendTime(chatSendRequest.getSendTime())
.unreadCount(1)
.build();
}

public ChatSendResponse toChatSendResponseWithEntity(ChatEntity chatEntity) {
return ChatSendResponse.builder()
.chatId(chatEntity.getChatId())
.chatType(chatEntity.getChatType().getValue())
.chatContent(chatEntity.getChatContent())
.roomId(chatEntity.getRoomId())
.senderId(chatEntity.getSenderId())
.senderName(chatEntity.getSenderName())
.receiverId(chatEntity.getReceiverId())
.sendTime(chatEntity.getSendTime())
.unreadCount(chatEntity.getUnreadCount())
.build();
}
}

This file was deleted.

This file was deleted.

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
package com.aliens.friendship.backend_chatting_server.chatting.dto.response;

import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;


@Data
@AllArgsConstructor
@NoArgsConstructor
@Builder
public class ChatSendResponse {
private Long chatId;
private Integer chatType;
private String chatContent;
private Long roomId;
private Long senderId;
private String senderName;
private Long receiverId;
private String sendTime;
private Integer unreadCount;
}
Loading