-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Browse files
Browse the repository at this point in the history
* [Feat/#84] Implement Chatting Room Feature * [Feat/#223] Implement Chatting Feature * [Feat/#223] Configure MongoDB client and implement message saving logic * [Feat/#223] Implement sorting chat rooms by unread message count * [Feat/#223] Implement logic to retrieve chat history * [Feat/#223] Update SendTime Logic * [Feat/#223] Enhance STOMP Authentication Handling * [Feat/#223] Update File Path * [Feat/#223] Add @SuperBuilder to ChatRoom and ChatRoomMember
- Loading branch information
1 parent
08fd735
commit 6070bf5
Showing
206 changed files
with
1,173 additions
and
4,513 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
57 changes: 57 additions & 0 deletions
57
src/main/java/com/example/waggle/domain/chat/config/StompHandler.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,57 @@ | ||
package com.example.waggle.domain.chat.config; | ||
|
||
import static org.springframework.messaging.simp.stomp.StompCommand.CONNECT; | ||
import static org.springframework.messaging.simp.stomp.StompCommand.SEND; | ||
import static org.springframework.messaging.simp.stomp.StompCommand.SUBSCRIBE; | ||
|
||
import com.example.waggle.domain.member.entity.Member; | ||
import com.example.waggle.domain.member.service.MemberQueryService; | ||
import com.example.waggle.global.exception.handler.MemberHandler; | ||
import com.example.waggle.global.payload.code.ErrorStatus; | ||
import com.example.waggle.global.security.service.TokenService; | ||
import lombok.RequiredArgsConstructor; | ||
import lombok.extern.slf4j.Slf4j; | ||
import org.springframework.core.Ordered; | ||
import org.springframework.core.annotation.Order; | ||
import org.springframework.messaging.Message; | ||
import org.springframework.messaging.MessageChannel; | ||
import org.springframework.messaging.simp.stomp.StompHeaderAccessor; | ||
import org.springframework.messaging.support.ChannelInterceptor; | ||
import org.springframework.messaging.support.MessageHeaderAccessor; | ||
import org.springframework.stereotype.Component; | ||
|
||
@Order(Ordered.HIGHEST_PRECEDENCE + 99) // 우선 순위를 높게 설정해서 SecurityFilter들 보다 앞서 실행되게 해준다. | ||
@Component | ||
@RequiredArgsConstructor | ||
@Slf4j | ||
public class StompHandler implements ChannelInterceptor { | ||
|
||
private final TokenService tokenService; | ||
private final MemberQueryService memberQueryService; | ||
|
||
@Override | ||
public Message<?> preSend(Message<?> message, MessageChannel channel) { | ||
StompHeaderAccessor accessor = MessageHeaderAccessor.getAccessor(message, StompHeaderAccessor.class); | ||
if (accessor.getCommand() == CONNECT || accessor.getCommand() == SEND || accessor.getCommand() == SUBSCRIBE) { | ||
Member member = getMemberByAccessToken(getAccessToken(accessor)); | ||
accessor.setUser(() -> member.getUsername()); | ||
} | ||
return message; | ||
} | ||
|
||
private String getAccessToken(StompHeaderAccessor accessor) { | ||
String token = accessor.getFirstNativeHeader("Authorization"); | ||
if (token != null && token.startsWith("Bearer ")) { | ||
return token.substring(7).trim(); | ||
} | ||
throw new MemberHandler(ErrorStatus.AUTH_IS_NULL); | ||
} | ||
|
||
private Member getMemberByAccessToken(String accessToken) { | ||
if (!tokenService.validateToken(accessToken)) { | ||
throw new MemberHandler(ErrorStatus.AUTH_INVALID_TOKEN); | ||
} | ||
String username = tokenService.getAuthentication(accessToken).getName(); | ||
return memberQueryService.getMemberByUsername(username); | ||
} | ||
} |
43 changes: 43 additions & 0 deletions
43
src/main/java/com/example/waggle/domain/chat/config/WebSocketConfig.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,43 @@ | ||
package com.example.waggle.domain.chat.config; | ||
|
||
import lombok.RequiredArgsConstructor; | ||
import org.springframework.context.annotation.Configuration; | ||
import org.springframework.messaging.simp.config.ChannelRegistration; | ||
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; | ||
import org.springframework.web.socket.config.annotation.WebSocketTransportRegistration; | ||
|
||
@RequiredArgsConstructor | ||
@EnableWebSocketMessageBroker | ||
@Configuration | ||
public class WebSocketConfig implements WebSocketMessageBrokerConfigurer { | ||
|
||
private final StompHandler stompHandler; | ||
|
||
@Override | ||
public void registerStompEndpoints(StompEndpointRegistry registry) { | ||
registry.addEndpoint("/ws/chat") | ||
.setAllowedOriginPatterns("*") | ||
.withSockJS(); | ||
} | ||
|
||
@Override | ||
public void configureMessageBroker(MessageBrokerRegistry registry) { | ||
registry.enableSimpleBroker("/subscribe"); // /subscribe/{chatRoomId}로 주제 구독 가능 | ||
registry.setApplicationDestinationPrefixes("/publish"); // /publish/message로 메시지 전송 컨트롤러 라우팅 가능 | ||
} | ||
|
||
@Override | ||
public void configureClientInboundChannel(ChannelRegistration registration) { | ||
registration.interceptors(stompHandler); | ||
} | ||
|
||
@Override | ||
public void configureWebSocketTransport(WebSocketTransportRegistration registry) { | ||
registry.setMessageSizeLimit(160 * 64 * 1024); | ||
registry.setSendTimeLimit(100 * 10000); | ||
registry.setSendBufferSizeLimit(3 * 512 * 1024); | ||
} | ||
} |
53 changes: 53 additions & 0 deletions
53
src/main/java/com/example/waggle/domain/chat/config/kafka/KafkaConsumerConfig.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,53 @@ | ||
//package com.example.waggle.domain.chat.config.kafka; | ||
// | ||
//import com.example.waggle.domain.chat.entity.MessageDto; | ||
//import com.google.common.collect.ImmutableMap; | ||
//import java.util.Map; | ||
//import lombok.RequiredArgsConstructor; | ||
//import org.apache.kafka.clients.consumer.ConsumerConfig; | ||
//import org.apache.kafka.common.serialization.StringDeserializer; | ||
//import org.springframework.context.annotation.Bean; | ||
//import org.springframework.context.annotation.Configuration; | ||
//import org.springframework.kafka.annotation.EnableKafka; | ||
//import org.springframework.kafka.config.ConcurrentKafkaListenerContainerFactory; | ||
//import org.springframework.kafka.core.ConsumerFactory; | ||
//import org.springframework.kafka.core.DefaultKafkaConsumerFactory; | ||
//import org.springframework.kafka.support.serializer.JsonDeserializer; | ||
// | ||
//@RequiredArgsConstructor | ||
//@EnableKafka | ||
//@Configuration | ||
//public class KafkaConsumerConfig { | ||
// | ||
// private final KafkaProperties kafkaProperties; | ||
// | ||
// | ||
// // KafkaListener 컨테이너 팩토리를 생성하는 Bean 메서드 | ||
// @Bean | ||
// ConcurrentKafkaListenerContainerFactory<String, MessageDto> kafkaListenerContainerFactory() { | ||
// ConcurrentKafkaListenerContainerFactory<String, MessageDto> factory = new ConcurrentKafkaListenerContainerFactory<>(); | ||
// factory.setConsumerFactory(consumerFactory()); | ||
// return factory; | ||
// } | ||
// | ||
// // Kafka ConsumerFactory를 생성하는 Bean 메서드 | ||
// @Bean | ||
// public ConsumerFactory<String, MessageDto> consumerFactory() { | ||
// JsonDeserializer<MessageDto> deserializer = new JsonDeserializer<>(); | ||
// // 패키지 신뢰 오류로 인해 모든 패키지를 신뢰하도록 작성 | ||
// deserializer.addTrustedPackages("*"); | ||
// | ||
// // Kafka Consumer 구성을 위한 설정값들을 설정 -> 변하지 않는 값이므로 ImmutableMap을 이용하여 설정 | ||
// Map<String, Object> consumerConfigurations = | ||
// ImmutableMap.<String, Object>builder() | ||
// .put(ConsumerConfig.BOOTSTRAP_SERVERS_CONFIG, kafkaProperties.getBroker()) | ||
// .put(ConsumerConfig.GROUP_ID_CONFIG, kafkaProperties.getGroupId()) | ||
// .put(ConsumerConfig.KEY_DESERIALIZER_CLASS_CONFIG, StringDeserializer.class) | ||
// .put(ConsumerConfig.VALUE_DESERIALIZER_CLASS_CONFIG, deserializer) | ||
// .put(ConsumerConfig.AUTO_OFFSET_RESET_CONFIG, "latest") | ||
// .build(); | ||
// | ||
// return new DefaultKafkaConsumerFactory<>(consumerConfigurations, new StringDeserializer(), deserializer); | ||
// } | ||
// | ||
//} |
46 changes: 46 additions & 0 deletions
46
src/main/java/com/example/waggle/domain/chat/config/kafka/KafkaProducerConfig.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,46 @@ | ||
//package com.example.waggle.domain.chat.config.kafka; | ||
// | ||
//import com.example.waggle.domain.chat.entity.MessageDto; | ||
//import com.google.common.collect.ImmutableMap; | ||
//import java.util.Map; | ||
//import lombok.RequiredArgsConstructor; | ||
//import org.apache.kafka.clients.producer.ProducerConfig; | ||
//import org.apache.kafka.common.serialization.StringSerializer; | ||
//import org.springframework.context.annotation.Bean; | ||
//import org.springframework.context.annotation.Configuration; | ||
//import org.springframework.kafka.annotation.EnableKafka; | ||
//import org.springframework.kafka.core.DefaultKafkaProducerFactory; | ||
//import org.springframework.kafka.core.KafkaTemplate; | ||
//import org.springframework.kafka.core.ProducerFactory; | ||
//import org.springframework.kafka.support.serializer.JsonSerializer; | ||
// | ||
//@RequiredArgsConstructor | ||
//@EnableKafka | ||
//@Configuration | ||
//public class KafkaProducerConfig { | ||
// | ||
// private final KafkaProperties kafkaProperties; | ||
// | ||
// // Kafka ProducerFactory를 생성하는 Bean 메서드 | ||
// @Bean | ||
// public ProducerFactory<String, MessageDto> producerFactory() { | ||
// return new DefaultKafkaProducerFactory<>(producerConfigurations()); | ||
// } | ||
// | ||
// // Kafka Producer 구성을 위한 설정값들을 포함한 맵을 반환하는 메서드 | ||
// @Bean | ||
// public Map<String, Object> producerConfigurations() { | ||
// return ImmutableMap.<String, Object>builder() | ||
// .put(ProducerConfig.BOOTSTRAP_SERVERS_CONFIG, kafkaProperties.getBroker()) | ||
// .put(ProducerConfig.KEY_SERIALIZER_CLASS_CONFIG, StringSerializer.class) | ||
// .put(ProducerConfig.VALUE_SERIALIZER_CLASS_CONFIG, JsonSerializer.class) | ||
// .build(); | ||
// } | ||
// | ||
// // KafkaTemplate을 생성하는 Bean 메서드 | ||
// @Bean | ||
// public KafkaTemplate<String, MessageDto> kafkaTemplate() { | ||
// return new KafkaTemplate<>(producerFactory()); | ||
// } | ||
// | ||
//} |
14 changes: 14 additions & 0 deletions
14
src/main/java/com/example/waggle/domain/chat/config/kafka/KafkaProperties.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,14 @@ | ||
package com.example.waggle.domain.chat.config.kafka; | ||
|
||
import lombok.Data; | ||
import org.springframework.boot.context.properties.ConfigurationProperties; | ||
import org.springframework.stereotype.Component; | ||
|
||
@Data | ||
@Component | ||
@ConfigurationProperties(prefix = "kafka.waggle") | ||
public class KafkaProperties { | ||
private String topic; | ||
private String groupId; | ||
private String broker; | ||
} |
39 changes: 39 additions & 0 deletions
39
src/main/java/com/example/waggle/domain/chat/entity/ChatMessage.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,39 @@ | ||
package com.example.waggle.domain.chat.entity; | ||
|
||
import jakarta.validation.constraints.NotBlank; | ||
import jakarta.validation.constraints.NotNull; | ||
import lombok.AllArgsConstructor; | ||
import lombok.Builder; | ||
import lombok.Getter; | ||
import lombok.NoArgsConstructor; | ||
import lombok.ToString; | ||
import org.springframework.data.annotation.Id; | ||
import org.springframework.data.mongodb.core.mapping.Document; | ||
|
||
@ToString | ||
@Document(collection = "chatMessage") | ||
@Getter | ||
@Builder | ||
@AllArgsConstructor | ||
@NoArgsConstructor | ||
public class ChatMessage { | ||
|
||
@Id | ||
private String id; | ||
|
||
@NotNull | ||
private Long chatRoomId; | ||
|
||
@NotNull | ||
private ChatMessageType chatMessageType; | ||
|
||
@NotBlank | ||
private String content; | ||
|
||
@NotBlank | ||
private String senderUserUrl; | ||
|
||
@NotNull | ||
private Long sendTime; | ||
|
||
} |
5 changes: 5 additions & 0 deletions
5
src/main/java/com/example/waggle/domain/chat/entity/ChatMessageType.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
package com.example.waggle.domain.chat.entity; | ||
|
||
public enum ChatMessageType { | ||
ENTER, EXIT, TALK | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
17 changes: 17 additions & 0 deletions
17
src/main/java/com/example/waggle/domain/chat/repository/ChatMessageRepository.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,17 @@ | ||
package com.example.waggle.domain.chat.repository; | ||
|
||
import com.example.waggle.domain.chat.entity.ChatMessage; | ||
import org.springframework.data.domain.Page; | ||
import org.springframework.data.domain.Pageable; | ||
import org.springframework.data.mongodb.repository.MongoRepository; | ||
import org.springframework.data.mongodb.repository.Query; | ||
|
||
public interface ChatMessageRepository extends MongoRepository<ChatMessage, String> { | ||
|
||
@Query(value = "{'chatRoomId': ?0, 'sendTime': {$gt: ?1}}", count = true) | ||
long countByChatRoomIdAndSendTimeAfter(Long chatRoomId, long lastAccessTime); | ||
|
||
@Query(value = "{'chatRoomId': ?0}", sort = "{'sendTime': -1}") | ||
Page<ChatMessage> findByChatRoomIdSortedBySendTimeDesc(Long chatRoomId, Pageable pageable); | ||
|
||
} |
Oops, something went wrong.