Skip to content

Commit

Permalink
Merge pull request #11 from kjungw1025/feat/chatting
Browse files Browse the repository at this point in the history
feat: 각 채팅방 별, 채팅 메시지 관리를 위한 DynamoDB 설정 및 기능 추가
  • Loading branch information
kjungw1025 authored Jan 29, 2024
2 parents 4a59841 + b647c1d commit 2ce00c6
Show file tree
Hide file tree
Showing 15 changed files with 314 additions and 27 deletions.
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

0 comments on commit 2ce00c6

Please sign in to comment.