diff --git a/build.gradle b/build.gradle index 9e642b0..164e5fd 100644 --- a/build.gradle +++ b/build.gradle @@ -24,70 +24,67 @@ repositories { } dependencies { + // Spring Boot Starter Dependencies implementation 'org.springframework.boot:spring-boot-starter-web' implementation 'org.springframework.boot:spring-boot-starter-data-jpa' - - implementation 'org.springframework.boot:spring-boot-starter-web' implementation 'org.springframework.boot:spring-boot-starter-aop' implementation 'org.springframework.boot:spring-boot-starter' - //open feign - implementation group: 'org.springframework.cloud', name: 'spring-cloud-starter-openfeign', version: '4.1.3' + // OpenFeign for HTTP client communication + implementation 'org.springframework.cloud:spring-cloud-starter-openfeign:4.1.3' implementation 'org.springframework.cloud:spring-cloud-commons:4.1.4' - //ELK 연동을 위한 외부 기술 + // ELK integration implementation 'org.springframework.boot:spring-boot-starter-data-elasticsearch' - implementation 'net.logstash.logback:logstash-logback-encoder:7.0' // Logstash용 로그백 엔코더 + implementation 'net.logstash.logback:logstash-logback-encoder:7.0' - //STOMP ( Websocket ) + // WebSocket implementation 'org.springframework.boot:spring-boot-starter-websocket' - implementation 'org.springframework.boot:spring-boot-starter' - //AWS 관련 + // AWS SDK implementation 'org.springframework.cloud:spring-cloud-starter-aws:2.2.6.RELEASE' - implementation 'com.amazonaws:aws-java-sdk-s3:1.12.563' // 최신 버전 확인 필요 - - //추후에 지울 것 - implementation 'org.springframework.boot:spring-boot-starter-thymeleaf' - // - - // Spring Security 의존성 추가 -// implementation 'org.springframework.boot:spring-boot-starter-security' + implementation 'com.amazonaws:aws-java-sdk-s3:1.12.563' - //DB 관련 설정들 + // Database & Serialization implementation 'com.fasterxml.jackson.datatype:jackson-datatype-jsr310' implementation 'org.springframework.boot:spring-boot-starter-data-mongodb' -// implementation 'org.springframework.boot:spring-boot-starter-data-redis' + runtimeOnly 'com.mysql:mysql-connector-j:8.0.33' - implementation 'org.springframework.boot:spring-boot-starter-data-jpa' - runtimeOnly 'com.mysql:mysql-connector-j' -//Swagger Ui + // OpenAPI Documentation implementation 'org.springdoc:springdoc-openapi-starter-webmvc-ui:2.0.2' - + // Jakarta Persistence (for JPA) implementation 'jakarta.persistence:jakarta.persistence-api:3.1.0' implementation 'jakarta.validation:jakarta.validation-api:3.0.2' - implementation 'org.springframework.boot:spring-boot-starter-security' + + // JWT for Authentication implementation 'io.jsonwebtoken:jjwt-api:0.11.5' implementation 'io.jsonwebtoken:jjwt-impl:0.11.5' - implementation 'io.jsonwebtoken:jjwt-jackson:0.11.5' // for JSON serialization/deserialization - implementation 'mysql:mysql-connector-java:8.0.33' + implementation 'io.jsonwebtoken:jjwt-jackson:0.11.5' + + // OAuth2 Client Support implementation 'org.springframework.boot:spring-boot-starter-oauth2-client' + + // Mail Support implementation 'org.springframework.boot:spring-boot-starter-mail' + + // Thymeleaf Template Engine implementation 'org.springframework.boot:spring-boot-starter-thymeleaf' - implementation 'org.springframework.boot:spring-boot-starter-webflux' // WebClient 및 WebFlux 지원 - // Redis 관련 의존성 - implementation 'org.springframework.boot:spring-boot-starter-data-redis' // redis - runtimeOnly 'com.mysql:mysql-connector-j' // sql + // WebFlux & Reactive Programming + implementation 'org.springframework.boot:spring-boot-starter-webflux' + // Redis Support + implementation 'org.springframework.boot:spring-boot-starter-data-redis' + + // Lombok for code generation compileOnly 'org.projectlombok:lombok' annotationProcessor 'org.projectlombok:lombok' - testCompileOnly 'org.projectlombok:lombok:1.18.28' // 테스트 + + // Testing Dependencies + testCompileOnly 'org.projectlombok:lombok:1.18.28' testImplementation 'org.springframework.boot:spring-boot-starter-test' testRuntimeOnly 'org.junit.platform:junit-platform-launcher' - - runtimeOnly 'com.mysql:mysql-connector-j' } tasks.named('test') { diff --git a/src/main/java/com/petmatz/api/chatting/ChatController.java b/src/main/java/com/petmatz/api/chatting/ChatController.java index 6bd4769..fce9cbe 100644 --- a/src/main/java/com/petmatz/api/chatting/ChatController.java +++ b/src/main/java/com/petmatz/api/chatting/ChatController.java @@ -2,6 +2,7 @@ import com.petmatz.api.chatting.dto.*; import com.petmatz.api.global.dto.Response; +import com.petmatz.api.global.utils.SendChatMessage; import com.petmatz.common.security.utils.JwtExtractProvider; import com.petmatz.domain.chatting.ChatMessageService; import com.petmatz.domain.chatting.ChatRoomService; @@ -33,24 +34,22 @@ public class ChatController { private final ChatMessageService chatService; - private final SimpMessagingTemplate simpMessagingTemplate; private final UserService userService; private final ChatRoomService chatRoomService; private final JwtExtractProvider jwtExtractProvider; + private final SendChatMessage sendChatMessage; @MessageMapping("/chat") public void sendPrivateMessage(ChatMessageRequest chatMessageRequest) { ChatMessageInfo chatMessageInfo = chatMessageRequest.of(); chatService.updateMessage(chatMessageInfo, chatMessageRequest.chatRoomId()); - String destination = "/topic/chat/" + chatMessageRequest.chatRoomId(); - simpMessagingTemplate.convertAndSend(destination, chatMessageInfo); + sendChatMessage.sendChatMessage(chatMessageRequest.chatRoomId(), chatMessageInfo); } @MessageMapping("/chat/{chatRoomId}/read") public void sendReadStatus(@Payload ChatReadStatusDirect chatReadStatusDirect, @DestinationVariable String chatRoomId) { - String destination = "/topic/chat/" + chatRoomId; - simpMessagingTemplate.convertAndSend(destination, chatReadStatusDirect); + sendChatMessage.sendChatMessage(chatRoomId, chatReadStatusDirect); } @@ -69,7 +68,7 @@ public Response selectChatMessage( @RequestParam(defaultValue = "1") int startPage ) { String userEmail = jwtExtractProvider.findAccountIdFromJwt(); - String receiverEmail = chatRoomService.selectChatRoomUserInfo(chatRoomId, userEmail); + String receiverEmail = chatRoomService.selectChatRoomUserEmail(chatRoomId, userEmail); Page chatMessageInfos = chatService.selectMessage(receiverEmail, chatRoomId, startPage, pageSize, lastFetchTimestamp); UserInfo userInfo = userService.selectUserInfo(receiverEmail); diff --git a/src/main/java/com/petmatz/api/chatting/MatchingController.java b/src/main/java/com/petmatz/api/chatting/MatchingController.java index 177906c..a0aa77c 100644 --- a/src/main/java/com/petmatz/api/chatting/MatchingController.java +++ b/src/main/java/com/petmatz/api/chatting/MatchingController.java @@ -47,11 +47,11 @@ public Response> chatRoomsList( ) { String userEmail = jwtExtractProvider.findAccountIdFromJwt(); - return Response.success( - chatRoomService.selectChatRoomList(pageSize, startPage, userEmail).stream() - .map(ChatRoomMetaDataInfoResponse::of) - .collect(Collectors.toList()) - ); + List chatRoomMetaDataInfoResponseList = chatRoomService.selectChatRoomList(pageSize, startPage, userEmail).stream() + .map(ChatRoomMetaDataInfoResponse::of) + .collect(Collectors.toList()); + + return Response.success(chatRoomMetaDataInfoResponseList); } @DeleteMapping diff --git a/src/main/java/com/petmatz/api/global/utils/SendChatMessage.java b/src/main/java/com/petmatz/api/global/utils/SendChatMessage.java new file mode 100644 index 0000000..e330202 --- /dev/null +++ b/src/main/java/com/petmatz/api/global/utils/SendChatMessage.java @@ -0,0 +1,28 @@ +package com.petmatz.api.global.utils; + +import com.petmatz.api.chatting.dto.ChatReadStatusDirect; +import com.petmatz.domain.chatting.dto.ChatMessageInfo; +import lombok.RequiredArgsConstructor; +import org.springframework.messaging.simp.SimpMessagingTemplate; +import org.springframework.stereotype.Component; + +@Component +@RequiredArgsConstructor +public class SendChatMessage { + + private final static String DESTINATION = "/topic/chat/"; + + private final SimpMessagingTemplate simpMessagingTemplate; + + + public void sendChatMessage(String chatRoomId, ChatMessageInfo chatMessageInfo) { + String destination = DESTINATION + chatRoomId; + simpMessagingTemplate.convertAndSend(destination, chatMessageInfo); + } + + public void sendChatMessage(String chatRoomId, ChatReadStatusDirect chatReadStatusDirect) { + String destination = DESTINATION + chatRoomId; + simpMessagingTemplate.convertAndSend(destination, chatReadStatusDirect); + } + +} diff --git a/src/main/java/com/petmatz/api/petmission/PetMissionController.java b/src/main/java/com/petmatz/api/petmission/PetMissionController.java index 6622a8f..ecd9035 100644 --- a/src/main/java/com/petmatz/api/petmission/PetMissionController.java +++ b/src/main/java/com/petmatz/api/petmission/PetMissionController.java @@ -1,6 +1,7 @@ package com.petmatz.api.petmission; import com.petmatz.api.global.dto.Response; +import com.petmatz.api.global.utils.SendChatMessage; import com.petmatz.api.petmission.dto.*; import com.petmatz.common.constants.PetMissionStatusZip; import com.petmatz.common.security.utils.JwtExtractProvider; @@ -29,9 +30,9 @@ public class PetMissionController { private final PetMissionService petMissionService; - private final SimpMessagingTemplate simpMessagingTemplate; private final ChatMessageService chatService; private final UserService userService; + private final SendChatMessage sendChatMessage; private final JwtExtractProvider jwtExtractProvider; @@ -53,16 +54,14 @@ public Response savePetMissionList(@RequestBody PetMissionRe chatService.updateMessage(petMissionRequest.ofto(), petMissionData, receiverEmail); - String destination = "/topic/chat/" + petMissionData.chatRoomId(); //채팅 메세지에 UUID 담아서 보내기 ChatMessageInfo chatMessageInfo = petMissionRequest.of(petMissionData.petMissionId(), careEmail,receiverEmail); PetMissionResponse petMissionResponse = PetMissionResponse.of(petMissionData); - simpMessagingTemplate.convertAndSend(destination, chatMessageInfo); + sendChatMessage.sendChatMessage(petMissionData.chatRoomId(), chatMessageInfo); return Response.success(petMissionResponse); } - //TODO 리펙토링 필수임 쿼리문 그지 같음 지금 @Operation(summary = "멍멍이의 부탁 리스트 조회", description = "멍멍이 리스트 조회 API") @GetMapping public Response> selectPetMissionList() { @@ -89,9 +88,8 @@ public Response updatePetMissionStatus(@RequestBody PetMissionUpdateReques String chatRoomId = petMissionService.selectChatRoomId(petMissionDetails.careEmail(), petMissionDetails.receiverEmail()); ChatMessageInfo chatMessageInfo = petMissionUpdateRequest.of(petMissionUpdateRequest.petMissionId()); chatService.updateMessage(petMissionUpdateRequest.of(petMissionUpdateRequest.petMissionId()), chatRoomId); - String destination = "/topic/chat/" + chatRoomId; //채팅 메세지에 UUID 담아서 보내기 - simpMessagingTemplate.convertAndSend(destination, chatMessageInfo); + sendChatMessage.sendChatMessage(chatRoomId, chatMessageInfo); return Response.success("업데이트가 정상적으로 되었습니다."); } return Response.success("업데이트가 정상적으로 되었습니다."); diff --git a/src/main/java/com/petmatz/domain/aws/Prefix.java b/src/main/java/com/petmatz/domain/aws/Prefix.java index a8b8716..0d9cdad 100644 --- a/src/main/java/com/petmatz/domain/aws/Prefix.java +++ b/src/main/java/com/petmatz/domain/aws/Prefix.java @@ -19,6 +19,7 @@ public static String returnKoreaName(String name) { return value.koreaName; } } + //TODO 에러 발생 시키기 return "없음"; } diff --git a/src/main/java/com/petmatz/domain/chatting/ChatMessageService.java b/src/main/java/com/petmatz/domain/chatting/ChatMessageService.java index eb41098..78eb83a 100644 --- a/src/main/java/com/petmatz/domain/chatting/ChatMessageService.java +++ b/src/main/java/com/petmatz/domain/chatting/ChatMessageService.java @@ -1,5 +1,6 @@ package com.petmatz.domain.chatting; +import com.petmatz.api.chatting.dto.ChatReadStatusDirect; import com.petmatz.domain.chatting.component.ChatMessageReader; import com.petmatz.domain.chatting.component.ChatMessageUpdater; import com.petmatz.domain.chatting.docs.ChatReadStatusDocs; @@ -10,6 +11,7 @@ import org.springframework.data.domain.Page; import org.springframework.data.domain.PageImpl; import org.springframework.data.domain.PageRequest; +import org.springframework.messaging.simp.SimpMessagingTemplate; import org.springframework.stereotype.Service; import java.time.LocalDateTime; @@ -20,10 +22,12 @@ @RequiredArgsConstructor public class ChatMessageService { + private final ChatMessageReader chatMessageReader; private final ChatMessageUpdater chatMessageUpdater; + public Page selectMessage(String receiver, String chatRoomsId, int pageNumber, int pageSize, LocalDateTime lastFetchTimestamp) { // 페이징된 메시지 가져오기 List chatMessageInfos = chatMessageReader.selectChatMessagesHistory(chatRoomsId, pageNumber, pageSize,lastFetchTimestamp); @@ -56,5 +60,4 @@ public void updateMessage(ChatMessagePetMissionInfo chatMessageInfo, PetMissionD chatMessageUpdater.updateMessage(chatMessageInfo.of(receiverEmail, petMissionData.petMissionId()),petMissionData.chatRoomId()); } - } diff --git a/src/main/java/com/petmatz/domain/chatting/ChatRoomService.java b/src/main/java/com/petmatz/domain/chatting/ChatRoomService.java index 971d4a5..a0f09b3 100644 --- a/src/main/java/com/petmatz/domain/chatting/ChatRoomService.java +++ b/src/main/java/com/petmatz/domain/chatting/ChatRoomService.java @@ -34,14 +34,10 @@ public class ChatRoomService { private final ChatRoomMetaDataDeleter chatRoomMetaDataDeleter; private final ChatReadStatusDeleter chatReadStatusDeleter; -// private final JwtExtractProvider jwtExtractProvider; - - //채팅방 신규 생성, 존재시 해당 ChatRoomID 반환 public long createdChatRoom(ChatRoomInfo chatRoomInfo) { Optional chatRoomEntity = chatRoomReader.selectChatRoom(chatRoomInfo); - System.out.println(chatRoomEntity.toString()); if (chatRoomEntity.isPresent()) { return chatRoomEntity.get().getId(); @@ -69,18 +65,18 @@ public List selectChatRoomList(int pageSize, int startPage private Map getUserList(List chatRoomNumber, String userEmail) { Map userList = new HashMap<>(); + for (UserToChatRoomEntity userToChatRoomEntity : chatRoomNumber) { - List participants = userToChatRoomEntity.getChatRoom().getParticipants(); - // 현재 사용자가 아닌 다른 사용자를 찾음 - for (UserToChatRoomEntity participant : participants) { - if (!participant.getUser().getAccountId().equals(userEmail)) { - IChatUserInfo userInfo = IChatUserInfo.of( - participant.getUser() - ); - userList.put(userToChatRoomEntity.getChatRoom().getId().toString(), userInfo); - break; // 다른 참여자를 하나만 찾으면 됨 - } - } + String chatRoomId = userToChatRoomEntity.getChatRoom().getId().toString(); + + // 현재 사용자가 아닌 다른 사용자를 찾기 위한 스트림 사용 + userToChatRoomEntity.getChatRoom().getParticipants().stream() + .filter(participant -> !participant.getUser().getAccountId().equals(userEmail)) + .findFirst() // 첫 번째로 조건을 만족하는 사용자 찾기 + .ifPresent(participant -> { + IChatUserInfo userInfo = IChatUserInfo.of(participant.getUser()); + userList.put(chatRoomId, userInfo); + }); } return userList; } @@ -108,7 +104,7 @@ public void deletRoom(String roomId) { chatReadStatusDeleter.deleteChatReadStatusDocs(strings, roomId); } - public String selectChatRoomUserInfo(String chatRoomId, String userEmail) { + public String selectChatRoomUserEmail(String chatRoomId, String userEmail) { List userEmailList = chatRoomReader.selectChatRoomUserList(chatRoomId); if (userEmailList.isEmpty()) { throw new NullPointerException("해당 채팅방이 존재하지 않습니다."); diff --git a/src/main/java/com/petmatz/infra/websocket/WebSocketEventListener.java b/src/main/java/com/petmatz/infra/websocket/WebSocketEventListener.java index e849a24..cc0c036 100644 --- a/src/main/java/com/petmatz/infra/websocket/WebSocketEventListener.java +++ b/src/main/java/com/petmatz/infra/websocket/WebSocketEventListener.java @@ -16,8 +16,7 @@ @RequiredArgsConstructor public class WebSocketEventListener { - private final static String PATTERN = "/topic/chat/(\\S+)/(\\S+)"; - private final static Pattern R = Pattern.compile(PATTERN); + private final static Pattern R = Pattern.compile("/topic/chat/(\\S+)/(\\S+)"); //TODO 여기 의존 바꿔야 할 수도