Skip to content

Commit

Permalink
Merge pull request #183 from Board-Buddy/feature/#29
Browse files Browse the repository at this point in the history
Feature/#29 채팅 관련 로직 구현
  • Loading branch information
runtime-zer0 authored Jul 31, 2024
2 parents e3ab27b + 42ff90c commit 9a4708c
Show file tree
Hide file tree
Showing 41 changed files with 1,311 additions and 11 deletions.
36 changes: 36 additions & 0 deletions src/main/java/sumcoda/boardbuddy/config/WebsocketConfig.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
package sumcoda.boardbuddy.config;

import org.springframework.context.annotation.Configuration;
import org.springframework.messaging.simp.config.MessageBrokerRegistry;
import org.springframework.web.socket.config.annotation.EnableWebSocketMessageBroker;
import org.springframework.web.socket.config.annotation.StompEndpointRegistry;
import org.springframework.web.socket.config.annotation.WebSocketMessageBrokerConfigurer;

@Configuration
@EnableWebSocketMessageBroker
public class WebsocketConfig implements WebSocketMessageBrokerConfigurer {

/**
* 메시지 브로커 설정
*
* @param registry 메시지 브로커 레지스트리
**/
@Override
public void configureMessageBroker(MessageBrokerRegistry registry) {
registry.setApplicationDestinationPrefixes("/api/chat");

registry.enableSimpleBroker("/api/chat/reception");
}

/**
* STOMP 엔드포인트 등록
*
* @param registry STOMP 엔드포인트 레지스트리
**/
@Override
public void registerStompEndpoints(StompEndpointRegistry registry) {
registry.addEndpoint("/api/chat/connection")
.setAllowedOriginPatterns("*")
.withSockJS();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
package sumcoda.boardbuddy.controller;

import lombok.RequiredArgsConstructor;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.messaging.handler.annotation.DestinationVariable;
import org.springframework.messaging.handler.annotation.MessageMapping;
import org.springframework.messaging.handler.annotation.Payload;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestAttribute;
import sumcoda.boardbuddy.dto.ChatMessageRequest;
import sumcoda.boardbuddy.dto.ChatMessageResponse;
import sumcoda.boardbuddy.dto.common.ApiResponse;
import sumcoda.boardbuddy.service.ChatMessageService;

import java.util.List;
import java.util.Map;

import static sumcoda.boardbuddy.builder.ResponseBuilder.buildSuccessResponseWithPairKeyData;

@Controller
@RequiredArgsConstructor
public class ChatMessageController {

private final ChatMessageService chatMessageService;

/**
* 특정 채팅방에 메세지 발행 및 전송
*
* @param chatRoomId 채팅방 Id
* @param publishDTO 발행할 메세지 내용 DTO
* @param username 메시지를 발행하는 사용자 이름
**/
@MessageMapping("/publication/{chatRoomId}")
public void publishMessage(
@DestinationVariable Long chatRoomId,
@Payload ChatMessageRequest.PublishDTO publishDTO,
@RequestAttribute String username) {

chatMessageService.publishMessage(chatRoomId, publishDTO, username);
}

/**
* 채팅방 메세지 내역 조회
*
* @param chatRoomId 채팅방 Id
* @param username 요청을 보낸 사용자 아이디
* @return 채팅방 메세지 내역
*/
@GetMapping("/api/chat/rooms/{chatRoomId}/messages")
public ResponseEntity<ApiResponse<Map<String, List<ChatMessageResponse.ChatMessageInfoDTO>>>> getChatMessages(
@PathVariable Long chatRoomId,
@RequestAttribute String username) {

List<ChatMessageResponse.ChatMessageInfoDTO> chatMessages = chatMessageService.findMessagesAfterMemberJoinedByChatRoomIdAndUsername(chatRoomId, username);

return buildSuccessResponseWithPairKeyData("chatMessages", chatMessages, "채팅 메세지들의 정보를 성공적으로 조회했습니다.", HttpStatus.OK);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
package sumcoda.boardbuddy.controller;

import lombok.RequiredArgsConstructor;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestAttribute;
import org.springframework.web.bind.annotation.RestController;
import sumcoda.boardbuddy.dto.ChatRoomResponse;
import sumcoda.boardbuddy.dto.GatherArticleResponse;
import sumcoda.boardbuddy.dto.common.ApiResponse;
import sumcoda.boardbuddy.service.ChatRoomService;
import sumcoda.boardbuddy.service.GatherArticleService;

import java.util.List;
import java.util.Map;

import static sumcoda.boardbuddy.builder.ResponseBuilder.buildSuccessResponseWithPairKeyData;

@RestController
@RequiredArgsConstructor
public class ChatRoomController {

private final ChatRoomService chatRoomService;

private final GatherArticleService gatherArticleService;

/**
* 채팅방 정보와 연관된 모집글 정보 조회
*
* @param chatRoomId 채팅방 Id
* @param gatherArticleId 모집글 Id
* @return 채팅방과 연관된 모집글 정보
**/
@GetMapping("/api/chat/rooms/{chatRoomId}/gather-articles/{gatherArticleId}")
public ResponseEntity<ApiResponse<Map<String, GatherArticleResponse.SummaryInfoDTO>>> getChatRoomGatherArticleInfo(@PathVariable Long chatRoomId,
@PathVariable Long gatherArticleId,
@RequestAttribute String username) {

GatherArticleResponse.SummaryInfoDTO gatherArticleSimpleInfo = gatherArticleService.getChatRoomGatherArticleSimpleInfo(chatRoomId, gatherArticleId, username);

return buildSuccessResponseWithPairKeyData("gatherArticleSimpleInfo", gatherArticleSimpleInfo, "모집글 정보를 성공적으로 조회했습니다.", HttpStatus.OK);
}

/**
* 특정 사용자가 참여한 채팅방 목록 조회
*
* @param username 조회하려는 사용자의 아이디
* @return 사용자가 참여한 채팅방 목록
*/
@GetMapping("/api/chat/rooms")
public ResponseEntity<ApiResponse<Map<String, List<ChatRoomResponse.ChatRoomDetailsDTO>>>> getChatRoomDetailsByUsername(@RequestAttribute String username) {
List<ChatRoomResponse.ChatRoomDetailsDTO> chatRoomDetailsList = chatRoomService.getChatRoomDetailsListByUsername(username);

return buildSuccessResponseWithPairKeyData("chatRoomDetailsList", chatRoomDetailsList, "참여중인 채팅방 목록을 성공적으로 조회했습니다.", HttpStatus.OK);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@
import org.springframework.web.bind.annotation.*;
import sumcoda.boardbuddy.dto.ParticipationApplicationResponse;
import sumcoda.boardbuddy.dto.common.ApiResponse;
import sumcoda.boardbuddy.service.ChatMessageService;
import sumcoda.boardbuddy.service.ChatRoomService;
import sumcoda.boardbuddy.service.ParticipationApplicationService;

import java.util.List;
Expand All @@ -19,6 +21,10 @@ public class ParticipationApplicationController {

private final ParticipationApplicationService participationApplicationService;

private final ChatRoomService chatRoomService;

private final ChatMessageService chatMessageService;

/**
* 모집글 참가 신청 요청
*
Expand All @@ -42,8 +48,15 @@ public ResponseEntity<ApiResponse<Void>> applyParticipation(@PathVariable Long g
**/
@PutMapping("/api/gather-articles/{gatherArticleId}/participation/{participationApplicationId}/approval")
public ResponseEntity<ApiResponse<Void>> approveParticipationApplication(@PathVariable Long gatherArticleId, @PathVariable Long participationApplicationId, @RequestAttribute String username, @RequestParam String applicantNickname) {

participationApplicationService.approveParticipationApplication(gatherArticleId, participationApplicationId, username);

// ChatRoom 입장 처리
Long chatRoomId = chatRoomService.enterChatRoom(gatherArticleId, applicantNickname);

// 채팅방 입장 메세지 발행 및 전송
chatMessageService.publishEnterChatMessage(chatRoomId, applicantNickname);

return buildSuccessResponseWithoutData(applicantNickname + "님의 참가 신청을 승인 했습니다.", HttpStatus.OK);
}

Expand All @@ -57,6 +70,7 @@ public ResponseEntity<ApiResponse<Void>> approveParticipationApplication(@PathVa
**/
@PutMapping("/api/gather-articles/{gatherArticleId}/participation/{participationApplicationId}/rejection")
public ResponseEntity<ApiResponse<Void>> rejectParticipationApplication(@PathVariable Long gatherArticleId, @PathVariable Long participationApplicationId, @RequestAttribute String username, @RequestParam String applicantNickname) {

participationApplicationService.rejectParticipationApplication(gatherArticleId, participationApplicationId, username);

return buildSuccessResponseWithoutData(applicantNickname + "님의 참가 신청을 거절 했습니다.", HttpStatus.OK);
Expand All @@ -70,7 +84,17 @@ public ResponseEntity<ApiResponse<Void>> rejectParticipationApplication(@PathVar
**/
@PutMapping("/api/gather-articles/{gatherArticleId}/participation")
public ResponseEntity<ApiResponse<Void>> cancelParticipationApplication(@PathVariable Long gatherArticleId, @RequestAttribute String username) {
participationApplicationService.cancelParticipationApplication(gatherArticleId, username);

Boolean isMemberParticipant = participationApplicationService.cancelParticipationApplication(gatherArticleId, username);

// 만약 참가 취소하는 사용자가 참가 승인으로 인하여, 모집글에 참여중인 사용자라면,
if (isMemberParticipant) {
// ChatRoom 퇴장 처리
Long chatRoomId = chatRoomService.leaveChatRoom(gatherArticleId, username);

// 채팅방 퇴장 메세지 발행 및 전송
chatMessageService.publishExitChatMessage(chatRoomId, username);
}

return buildSuccessResponseWithoutData("해당 모집글의 참가 신청을 취소했습니다.", HttpStatus.OK);
}
Expand All @@ -84,7 +108,9 @@ public ResponseEntity<ApiResponse<Void>> cancelParticipationApplication(@PathVar
**/
@GetMapping("/api/gather-articles/{gatherArticleId}/participation")
public ResponseEntity<ApiResponse<Map<String, List<ParticipationApplicationResponse.InfoDTO>>>> getParticipationAppliedMemberList(@PathVariable Long gatherArticleId, @RequestAttribute String username) {

List<ParticipationApplicationResponse.InfoDTO> participationAppliedMemberList = participationApplicationService.getParticipationAppliedMemberList(gatherArticleId, username);

return buildSuccessResponseWithPairKeyData("participationAppliedMemberList", participationAppliedMemberList, "해당 모집글의 참가 신청 목록을 성공적으로 조회했습니다.", HttpStatus.OK);
}
}
20 changes: 20 additions & 0 deletions src/main/java/sumcoda/boardbuddy/dto/ChatMessageRequest.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
package sumcoda.boardbuddy.dto;

import lombok.AccessLevel;
import lombok.Builder;
import lombok.Getter;
import lombok.NoArgsConstructor;

public class ChatMessageRequest {

@Getter
@NoArgsConstructor(access = AccessLevel.PROTECTED)
public static class PublishDTO {
private String content;

@Builder
public PublishDTO(String content) {
this.content = content;
}
}
}
72 changes: 72 additions & 0 deletions src/main/java/sumcoda/boardbuddy/dto/ChatMessageResponse.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
package sumcoda.boardbuddy.dto;

import com.fasterxml.jackson.annotation.JsonFormat;
import lombok.Builder;
import lombok.Getter;
import lombok.NoArgsConstructor;
import sumcoda.boardbuddy.enumerate.MessageType;

import java.time.LocalDateTime;

public class ChatMessageResponse {

@Getter
@NoArgsConstructor
public static class ChatMessageInfoDTO {

private String content;

private String nickname;

private String profileImageS3SavedURL;

private Integer rank;

private MessageType messageType;

@JsonFormat(pattern = "yyyy-MM-dd HH:mm")
private LocalDateTime sentAt;

@Builder
public ChatMessageInfoDTO(String content, String nickname, String profileImageS3SavedURL, Integer rank, MessageType messageType, LocalDateTime sentAt) {
this.content = content;
this.nickname = nickname;
this.profileImageS3SavedURL = profileImageS3SavedURL;
this.rank = rank;
this.messageType = messageType;
this.sentAt = sentAt;
}

}

@Getter
@NoArgsConstructor
public static class EnterOrExitMessageInfoDTO {

private String content;

private MessageType messageType;

@Builder
public EnterOrExitMessageInfoDTO(String content, MessageType messageType) {
this.content = content;
this.messageType = messageType;
}
}

@Getter
@NoArgsConstructor
public static class LatestChatMessageInfoDTO {

private String content;

@JsonFormat(pattern = "yyyy-MM-dd HH:mm")
private LocalDateTime sentAt;

@Builder
public LatestChatMessageInfoDTO(String content, LocalDateTime sentAt) {
this.content = content;
this.sentAt = sentAt;
}
}
}
60 changes: 60 additions & 0 deletions src/main/java/sumcoda/boardbuddy/dto/ChatRoomResponse.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
package sumcoda.boardbuddy.dto;

import lombok.Builder;
import lombok.Getter;
import lombok.NoArgsConstructor;
import sumcoda.boardbuddy.enumerate.MemberChatRoomRole;

import java.time.LocalDateTime;

public class ChatRoomResponse {

@Getter
@NoArgsConstructor
public static class InfoDTO {

private Long id;

private LocalDateTime joinedAt;

private MemberChatRoomRole memberChatRoomRole;

@Builder
public InfoDTO(Long id, LocalDateTime joinedAt, MemberChatRoomRole memberChatRoomRole) {
this.id = id;
this.joinedAt = joinedAt;
this.memberChatRoomRole = memberChatRoomRole;
}
}

@Getter
@NoArgsConstructor
public static class ValidateDTO {

private Long id;

@Builder
public ValidateDTO(Long id) {
this.id = id;
}
}

@Getter
@NoArgsConstructor
public static class ChatRoomDetailsDTO {

private Long chatRoomId;

private GatherArticleResponse.SimpleInfoDTO gatherArticleSimpleInfo;

private ChatMessageResponse.LatestChatMessageInfoDTO latestChatMessageInfoDTO;

@Builder
public ChatRoomDetailsDTO(Long chatRoomId, GatherArticleResponse.SimpleInfoDTO gatherArticleSimpleInfo, ChatMessageResponse.LatestChatMessageInfoDTO latestChatMessageInfoDTO) {
this.chatRoomId = chatRoomId;
this.gatherArticleSimpleInfo = gatherArticleSimpleInfo;
this.latestChatMessageInfoDTO = latestChatMessageInfoDTO;
}
}

}
Loading

0 comments on commit 9a4708c

Please sign in to comment.