Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: 각 채팅방 별, 채팅 메시지 관리를 위한 DynamoDB 설정 및 기능 추가 #11

Merged
merged 7 commits into from
Jan 29, 2024
4 changes: 4 additions & 0 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,10 @@ dependencies {
// gson
implementation 'com.google.code.gson:gson:2.9.0'

// dynamodb
implementation 'com.amazonaws:aws-java-sdk-s3:1.12.268'
implementation 'io.github.boostchicken:spring-data-dynamodb:5.2.5'

// test
testImplementation 'org.springframework.boot:spring-boot-starter-test'
testImplementation 'org.springframework.security:spring-security-test'
Expand Down
1 change: 0 additions & 1 deletion lombok.config
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
lombok.copyableAnnotations += org.springframework.beans.factory.annotation.Value
lombok.copyableannotations += com.dku.council.global.config.webclient.ChromeAgentWebClient
lombok.anyConstructor.addConstructorProperties = true
lombok.Setter.flagUsage = error
lombok.data.flagUsage = error
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,9 @@
import com.dku.council.domain.chat.model.dto.response.ResponseChatDto;
import com.dku.council.domain.chat.service.ChatService;
import com.dku.council.domain.chat.service.MessageSender;
import com.dku.council.domain.chatmessage.model.entity.ChatRoomMessage;
import com.dku.council.domain.chatmessage.service.ChatRoomMessageService;
import com.dku.council.domain.user.service.UserService;
import com.dku.council.global.auth.role.UserAuth;
import io.swagger.v3.oas.annotations.tags.Tag;
import lombok.RequiredArgsConstructor;
Expand All @@ -18,7 +21,9 @@
import org.springframework.messaging.simp.SimpMessageSendingOperations;
import org.springframework.messaging.simp.stomp.StompHeaderAccessor;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.socket.messaging.SessionDisconnectEvent;

Expand All @@ -39,7 +44,9 @@ public class ChatController {
*/
private final SimpMessageSendingOperations template;

private final UserService userService;
private final ChatService chatService;
private final ChatRoomMessageService chatRoomMessageService;
private final MessageSender sender;

/**
Expand All @@ -61,16 +68,22 @@ public void enterUser(@Payload RequestChatDto chat,
String username = chatService.addUser(chat.getRoomId(), chat.getSender());
log.info("enterUser에서 uuid " + username);
log.info("enterUser에서 roomId " + chat.getRoomId());
log.info("enterUser에서 userId " + chat.getUserId());

// 반환 결과를 socket session 에 userUUID 로 저장
headerAccessor.getSessionAttributes().put("username", username);
headerAccessor.getSessionAttributes().put("userId", chat.getUserId());
headerAccessor.getSessionAttributes().put("roomId", chat.getRoomId());

String enterMessage = chat.getSender() + " 님 입장!!";
// 입장 메시지 저장
chatRoomMessageService.create(chat.getRoomId(), chat.getType().toString(), chat.getUserId(), chat.getSender(), enterMessage);

Message message = Message.builder()
.type(chat.getType())
.roomId(chat.getRoomId())
.sender(chat.getSender())
.message(chat.getSender() + " 님 입장!!")
.message(enterMessage)
.build();

sender.send(topic, message);
Expand All @@ -85,6 +98,8 @@ public void enterUser(@Payload RequestChatDto chat,
public void sendMessage(@Payload RequestChatDto chat) {
log.info("CHAT {}", chat);

chatRoomMessageService.create(chat.getRoomId(), chat.getType().toString(), chat.getUserId(), chat.getSender(), chat.getMessage());

Message message = Message.builder()
.type(chat.getType())
.roomId(chat.getRoomId())
Expand All @@ -108,6 +123,7 @@ public void webSocketDisconnectListener(SessionDisconnectEvent event) {

// stomp 세션에 있던 username과 roomId 를 확인해서 채팅방 유저 리스트와 room 에서 해당 유저를 삭제
String username = (String) headerAccessor.getSessionAttributes().get("username");
Long userId = (Long) headerAccessor.getSessionAttributes().get("userId");
String roomId = (String) headerAccessor.getSessionAttributes().get("roomId");
log.info("퇴장 controller에서 uuid " + username);
log.info("퇴장 controller에서 roomId " + roomId);
Expand All @@ -123,12 +139,15 @@ public void webSocketDisconnectListener(SessionDisconnectEvent event) {
if (username != null) {
log.info("User Disconnected : ", username);

String exitMessage = username + " 님 퇴장!!";
// 퇴장 메시지 저장
chatRoomMessageService.create(roomId, MessageType.LEAVE.toString(), userId, username, exitMessage);
// builder 어노테이션 활용
Message message = Message.builder()
.type(MessageType.LEAVE)
.sender(username)
.roomId(roomId)
.message(username + " 님 퇴장!!")
.message(exitMessage)
.build();

sender.send(topic, message);
Expand All @@ -146,4 +165,12 @@ public void webSocketDisconnectListener(SessionDisconnectEvent event) {
public List<String> userList(String roomId) {
return chatService.getUserList(roomId);
}


@GetMapping("/chat/message/list")
@UserAuth
@ResponseBody
public List<ChatRoomMessage> list(@RequestParam("roomId") String roomId) {
return chatRoomMessageService.findAllChatRoomMessages(roomId);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

import com.dku.council.domain.chat.model.dto.response.ResponseChatRoomDto;
import com.dku.council.domain.chat.service.ChatService;
import com.dku.council.domain.chatmessage.service.ChatRoomMessageService;
import com.dku.council.domain.user.model.dto.response.ResponseUserInfoForChattingDto;
import com.dku.council.domain.user.service.UserService;
import com.dku.council.global.auth.jwt.AppAuthentication;
Expand All @@ -24,6 +25,7 @@ public class ChatRoomController {
private final ChatService chatService;

private final UserService userService;
private final ChatRoomMessageService chatRoomMessageService;

/**
* 채팅방 리스트 화면
Expand Down Expand Up @@ -111,6 +113,8 @@ public boolean confirmPwd(@PathVariable String roomId,
@UserAuth
public String delChatRoom(@PathVariable String roomId, AppAuthentication auth){

chatRoomMessageService.deleteChatRoomMessages(roomId);

// roomId(UUID 값) 기준으로 채팅방 삭제
chatService.delChatRoom(auth.getUserId(), roomId, auth.isAdmin());

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@ public class RequestChatDto {

private final String roomId;

private final Long userId;

private final String sender;

private final String message;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
package com.dku.council.domain.chatmessage.model;

import com.amazonaws.services.dynamodbv2.datamodeling.DynamoDBHashKey;
import com.amazonaws.services.dynamodbv2.datamodeling.DynamoDBRangeKey;
import com.amazonaws.services.dynamodbv2.datamodeling.DynamoDBTypeConverted;
import com.dku.council.global.config.DynamoDBConfig;
import lombok.Getter;
import lombok.Setter;

import java.io.Serializable;
import java.time.LocalDateTime;

@Getter
@Setter
public class ChatRoomMessageId implements Serializable {
private static final long serialVersionUID = 1L;

@DynamoDBHashKey
private String roomId;

@DynamoDBRangeKey
@DynamoDBTypeConverted(converter = DynamoDBConfig.LocalDateTimeConverter.class)
private LocalDateTime createdAt;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
package com.dku.council.domain.chatmessage.model.entity;

import com.amazonaws.services.dynamodbv2.datamodeling.*;
import com.dku.council.domain.chatmessage.model.ChatRoomMessageId;
import com.dku.council.global.config.DynamoDBConfig;
import lombok.AccessLevel;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
import org.springframework.data.annotation.Id;

import java.time.LocalDateTime;
@DynamoDBTable(tableName = "ChatRoomMessage")
@Getter
@Setter
@NoArgsConstructor()
public class ChatRoomMessage {

@Id
@Getter(AccessLevel.NONE)
@Setter(AccessLevel.NONE)
private ChatRoomMessageId chatRoomMessageId;

@DynamoDBHashKey(attributeName = "roomId")
public String getRoomId() {
return chatRoomMessageId != null ? chatRoomMessageId.getRoomId() : null;
}

public void setRoomId(String roomId) {
if (chatRoomMessageId == null) {
chatRoomMessageId = new ChatRoomMessageId();
}
chatRoomMessageId.setRoomId(roomId);
}

@DynamoDBRangeKey(attributeName = "createdAt")
@DynamoDBTypeConverted(converter = DynamoDBConfig.LocalDateTimeConverter.class)
public LocalDateTime getCreatedAt() {
return chatRoomMessageId != null ? chatRoomMessageId.getCreatedAt() : null;
}

@DynamoDBTypeConverted(converter = DynamoDBConfig.LocalDateTimeConverter.class)
public void setCreatedAt(LocalDateTime createdAt) {
if (chatRoomMessageId == null) {
chatRoomMessageId = new ChatRoomMessageId();
}
chatRoomMessageId.setCreatedAt(createdAt);
}

@DynamoDBAttribute
private String messageType;

@DynamoDBAttribute
private Long userId;

@DynamoDBAttribute
private String userNickname;

@DynamoDBAttribute
private String content;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
package com.dku.council.domain.chatmessage.repository;

import com.dku.council.domain.chatmessage.model.ChatRoomMessageId;
import com.dku.council.domain.chatmessage.model.entity.ChatRoomMessage;
import org.socialsignin.spring.data.dynamodb.repository.EnableScan;
import org.springframework.data.repository.CrudRepository;
import org.springframework.stereotype.Repository;

import java.util.List;

@EnableScan
@Repository
public interface ChatRoomMessageRepository extends CrudRepository<ChatRoomMessage, ChatRoomMessageId> {
List<ChatRoomMessage> findAllByRoomIdOrderByCreatedAtAsc(String roomId);

void deleteAllByRoomId(String roomId);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
package com.dku.council.domain.chatmessage.service;

import com.dku.council.domain.chatmessage.model.entity.ChatRoomMessage;
import com.dku.council.domain.chatmessage.repository.ChatRoomMessageRepository;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;

import java.time.LocalDateTime;
import java.time.ZoneId;
import java.util.List;

@Service
@RequiredArgsConstructor
@Slf4j
public class ChatRoomMessageService {

private final ZoneId seoulZoneId = ZoneId.of("Asia/Seoul");

private final ChatRoomMessageRepository chatRoomMessageRepository;

public void create(String roomId,
String messageType,
Long userId,
String userNickname,
String content) {

ChatRoomMessage chatRoomMessage = new ChatRoomMessage();
chatRoomMessage.setRoomId(roomId);
chatRoomMessage.setMessageType(messageType);
chatRoomMessage.setUserId(userId);
chatRoomMessage.setUserNickname(userNickname);
chatRoomMessage.setContent(content);
chatRoomMessage.setCreatedAt(LocalDateTime.now().atZone(seoulZoneId).toLocalDateTime());

chatRoomMessageRepository.save(chatRoomMessage);
}

public List<ChatRoomMessage> findAllChatRoomMessages(String roomId) {
return chatRoomMessageRepository.findAllByRoomIdOrderByCreatedAtAsc(roomId);
}

public void deleteChatRoomMessages(String roomId) {
chatRoomMessageRepository.deleteAllByRoomId(roomId);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,7 @@
@Getter
@RequiredArgsConstructor
public class ResponseUserInfoForChattingDto {
private final String studentId;
private final String username;
private final Long userId;
private final String nickname;
private final boolean isAdmin;
}
Original file line number Diff line number Diff line change
Expand Up @@ -120,6 +120,6 @@ public void isDkuChecked(Long userId) {

public ResponseUserInfoForChattingDto getUserInfoForChatting(Long memberId) {
User user = userRepository.findById(memberId).orElseThrow(UserNotFoundException::new);
return new ResponseUserInfoForChattingDto(user.getStudentId(), user.getName(), user.getNickname(), user.getUserRole().isAdmin());
return new ResponseUserInfoForChattingDto(user.getId(), user.getNickname(), user.getUserRole().isAdmin());
}
}
67 changes: 67 additions & 0 deletions src/main/java/com/dku/council/global/config/DynamoDBConfig.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
package com.dku.council.global.config;

import com.amazonaws.auth.AWSCredentials;
import com.amazonaws.auth.AWSCredentialsProvider;
import com.amazonaws.auth.AWSStaticCredentialsProvider;
import com.amazonaws.auth.BasicAWSCredentials;
import com.amazonaws.services.dynamodbv2.AmazonDynamoDB;
import com.amazonaws.services.dynamodbv2.AmazonDynamoDBClientBuilder;
import com.amazonaws.services.dynamodbv2.datamodeling.DynamoDBTypeConverter;
import org.socialsignin.spring.data.dynamodb.repository.config.EnableDynamoDBRepositories;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.FilterType;
import org.springframework.stereotype.Repository;

import java.time.LocalDateTime;
import java.time.ZoneId;
import java.util.Date;

@Configuration
@EnableDynamoDBRepositories(basePackages = {"com.dku.council.domain.chatmessage.repository"},
includeFilters = @ComponentScan.Filter(type = FilterType.ANNOTATION, classes = Repository.class))
public class DynamoDBConfig {

@Value("${aws.dynamodb.accessKey}")
private String awsAccessKey;

@Value("${aws.dynamodb.secretKey}")
private String awsSecretKey;

@Value("${aws.dynamodb.region}")
private String awsRegion;

public AWSCredentials amazonAWSCredentials() {
return new BasicAWSCredentials(awsAccessKey, awsSecretKey);
}

public AWSCredentialsProvider amazonAWSCredentialsProvider() {
return new AWSStaticCredentialsProvider(amazonAWSCredentials());
}

@Bean
public AmazonDynamoDB amazonDynamoDB() {
return AmazonDynamoDBClientBuilder.standard().withCredentials(amazonAWSCredentialsProvider())
.withRegion(awsRegion).build();
}

/**
* Java DynamoDB SDK가 Java의 기본 Date 타입만 허용하므로
* LocalDateTimeType과 Date를 상호 변환할 수 있는 컨버터 추가
*/
public static class LocalDateTimeConverter implements DynamoDBTypeConverter<Date, LocalDateTime> {
@Override
public Date convert(LocalDateTime source) {
ZoneId seoulZoneId = ZoneId.of("Asia/Seoul");
return Date.from(source.atZone(seoulZoneId).toInstant());
}

@Override
public LocalDateTime unconvert(Date source) {
return source.toInstant().atZone(ZoneId.of("Asia/Seoul")).toLocalDateTime();
}
}

}
Loading
Loading