diff --git a/MathCaptain/weakness/src/main/java/MathCaptain/weakness/domain/Chat/controller/ChatController.java b/MathCaptain/weakness/src/main/java/MathCaptain/weakness/domain/Chat/controller/ChatController.java new file mode 100644 index 0000000..3ac984e --- /dev/null +++ b/MathCaptain/weakness/src/main/java/MathCaptain/weakness/domain/Chat/controller/ChatController.java @@ -0,0 +1,34 @@ +package MathCaptain.weakness.domain.Chat.controller; + +import MathCaptain.weakness.domain.Chat.dto.request.ChatRequest; +import MathCaptain.weakness.domain.Chat.dto.response.ChatResponse; +import MathCaptain.weakness.domain.Chat.service.ChatService; +import MathCaptain.weakness.domain.User.entity.Users; +import MathCaptain.weakness.global.annotation.LoginUser; +import lombok.RequiredArgsConstructor; +import org.springframework.messaging.handler.annotation.MessageMapping; +import org.springframework.messaging.simp.SimpMessagingTemplate; +import org.springframework.web.bind.annotation.RestController; +import java.util.List; + +@RestController +@RequiredArgsConstructor +public class ChatController { + + private final SimpMessagingTemplate template; + + private final ChatService chatService; + + @MessageMapping("/chat.send") // 클라이언트 → /app/chat.send + public void handleChat(ChatRequest request, @LoginUser Users loginUser) { + // 1) 유저 메시지 저장 + 클라이언트에게 에코 + ChatResponse chatResponse = chatService.saveUserMessage(loginUser, request); + template.convertAndSend("/sub/" + loginUser.getUserId(), chatResponse); + + // 2) LLM 호출 & 응답 저장 + 브로드캐스트 (동기 방식) + List llmResponses = chatService.askAI(loginUser, request); + llmResponses.forEach(aiResp -> + template.convertAndSend("/sub/" + loginUser.getUserId(), aiResp) + ); + } +} diff --git a/MathCaptain/weakness/src/main/java/MathCaptain/weakness/domain/Chat/dto/request/ChatRequest.java b/MathCaptain/weakness/src/main/java/MathCaptain/weakness/domain/Chat/dto/request/ChatRequest.java new file mode 100644 index 0000000..c5878e5 --- /dev/null +++ b/MathCaptain/weakness/src/main/java/MathCaptain/weakness/domain/Chat/dto/request/ChatRequest.java @@ -0,0 +1,19 @@ +package MathCaptain.weakness.domain.Chat.dto.request; + +import lombok.Getter; +import lombok.NoArgsConstructor; + +@Getter +@NoArgsConstructor +public class ChatRequest { + + private String message; + + private ChatRequest(String message) { + this.message = message; + } + + public static ChatRequest of(String message) { + return new ChatRequest(message); + } +} diff --git a/MathCaptain/weakness/src/main/java/MathCaptain/weakness/domain/Chat/dto/request/LLMRequest.java b/MathCaptain/weakness/src/main/java/MathCaptain/weakness/domain/Chat/dto/request/LLMRequest.java new file mode 100644 index 0000000..90bde8d --- /dev/null +++ b/MathCaptain/weakness/src/main/java/MathCaptain/weakness/domain/Chat/dto/request/LLMRequest.java @@ -0,0 +1,29 @@ +package MathCaptain.weakness.domain.Chat.dto.request; + +import MathCaptain.weakness.domain.Chat.entity.Chat; +import MathCaptain.weakness.domain.User.entity.Users; +import lombok.Getter; +import lombok.NoArgsConstructor; + +import java.util.List; + +@Getter +@NoArgsConstructor +public class LLMRequest { + + private Long userId; + + private String message; + + private List history; + + private LLMRequest(Long userId, String message, List history) { + this.userId = userId; + this.message = message; + this.history = history; + } + + public static LLMRequest of(Users loginUser, ChatRequest request, List history) { + return new LLMRequest(loginUser.getUserId(), request.getMessage(), history); + } +} diff --git a/MathCaptain/weakness/src/main/java/MathCaptain/weakness/domain/Chat/dto/response/ChatResponse.java b/MathCaptain/weakness/src/main/java/MathCaptain/weakness/domain/Chat/dto/response/ChatResponse.java new file mode 100644 index 0000000..b0f5a99 --- /dev/null +++ b/MathCaptain/weakness/src/main/java/MathCaptain/weakness/domain/Chat/dto/response/ChatResponse.java @@ -0,0 +1,37 @@ +package MathCaptain.weakness.domain.Chat.dto.response; + +import MathCaptain.weakness.domain.Chat.entity.Chat; +import MathCaptain.weakness.domain.common.enums.ChatRole; +import lombok.Getter; +import lombok.NoArgsConstructor; + +import java.time.LocalDateTime; + +@Getter +@NoArgsConstructor +public class ChatResponse { + + private Long userId; + + private ChatRole role; + + private String message; + + private LocalDateTime sendTime; + + private ChatResponse(Long userId, ChatRole role, String message, LocalDateTime sendTime) { + this.userId = userId; + this.role = role; + this.message = message; + this.sendTime = sendTime; + } + + public static ChatResponse of(Chat chat) { + return new ChatResponse( + chat.getUserId(), + chat.getRole(), + chat.getMessage(), + chat.getSendTime() + ); + } +} diff --git a/MathCaptain/weakness/src/main/java/MathCaptain/weakness/domain/Chat/entity/Chat.java b/MathCaptain/weakness/src/main/java/MathCaptain/weakness/domain/Chat/entity/Chat.java index 63e71c5..fd6c95d 100644 --- a/MathCaptain/weakness/src/main/java/MathCaptain/weakness/domain/Chat/entity/Chat.java +++ b/MathCaptain/weakness/src/main/java/MathCaptain/weakness/domain/Chat/entity/Chat.java @@ -1,8 +1,12 @@ package MathCaptain.weakness.domain.Chat.entity; -import MathCaptain.weakness.domain.Chat.enums.ChatRole; +import MathCaptain.weakness.domain.Chat.dto.request.ChatRequest; +import MathCaptain.weakness.domain.Chat.dto.response.ChatResponse; +import MathCaptain.weakness.domain.User.entity.Users; +import MathCaptain.weakness.domain.common.enums.ChatRole; import jakarta.persistence.*; import lombok.AccessLevel; +import lombok.Builder; import lombok.Getter; import lombok.NoArgsConstructor; import org.springframework.data.annotation.CreatedDate; @@ -30,4 +34,23 @@ public class Chat { @CreatedDate @Column(updatable = false) private LocalDateTime sendTime; + + @Builder + private Chat(Long userId, ChatRole role, String message) { + this.userId = userId; + this.role = role; + this.message = message; + } + + public static Chat of(Users user, ChatRequest request, ChatRole role) { + return new Chat(user.getUserId(), role, request.getMessage()); + } + + public static Chat of(ChatResponse response) { + return Chat.builder() + .userId(response.getUserId()) + .role(ChatRole.ASSISTANT) + .message(response.getMessage()) + .build(); + } } diff --git a/MathCaptain/weakness/src/main/java/MathCaptain/weakness/domain/Chat/repository/ChatRepository.java b/MathCaptain/weakness/src/main/java/MathCaptain/weakness/domain/Chat/repository/ChatRepository.java index e996a51..83fc8de 100644 --- a/MathCaptain/weakness/src/main/java/MathCaptain/weakness/domain/Chat/repository/ChatRepository.java +++ b/MathCaptain/weakness/src/main/java/MathCaptain/weakness/domain/Chat/repository/ChatRepository.java @@ -4,6 +4,9 @@ import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.stereotype.Repository; +import java.util.List; + @Repository public interface ChatRepository extends JpaRepository { + List findAllByUserIdOrderBySendTimeAsc(Long userId); } diff --git a/MathCaptain/weakness/src/main/java/MathCaptain/weakness/domain/Chat/service/ChatService.java b/MathCaptain/weakness/src/main/java/MathCaptain/weakness/domain/Chat/service/ChatService.java new file mode 100644 index 0000000..a5a35e2 --- /dev/null +++ b/MathCaptain/weakness/src/main/java/MathCaptain/weakness/domain/Chat/service/ChatService.java @@ -0,0 +1,44 @@ +package MathCaptain.weakness.domain.Chat.service; + +import MathCaptain.weakness.domain.Chat.dto.request.ChatRequest; +import MathCaptain.weakness.domain.Chat.dto.response.ChatResponse; +import MathCaptain.weakness.domain.Chat.entity.Chat; +import MathCaptain.weakness.domain.Chat.repository.ChatRepository; +import MathCaptain.weakness.domain.User.entity.Users; +import MathCaptain.weakness.domain.common.enums.ChatRole; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.util.List; +import java.util.UUID; + +@Service +@RequiredArgsConstructor +@Transactional(readOnly = true) +public class ChatService { + + public static final ChatRole USER = ChatRole.USER; + private final ChatRepository chatRepository; + private final LLMClient llm; + + @Transactional + public ChatResponse saveUserMessage(Users loginUser, ChatRequest request) { + Chat message = Chat.of(loginUser, request, USER); + Chat saved = chatRepository.save(message); + return ChatResponse.of(saved); + } + + public List askAI(Users loginUser, ChatRequest request) { + List history = chatRepository.findAllByUserIdOrderBySendTimeAsc(loginUser.getUserId()); + List aiChats = llm.call(loginUser, history, request); + return aiChats.stream() + .map(this::storeAndTransform) + .toList(); + } + + private ChatResponse storeAndTransform(Chat message) { + Chat saved = chatRepository.save(message); + return ChatResponse.of(saved); + } +} diff --git a/MathCaptain/weakness/src/main/java/MathCaptain/weakness/domain/Chat/service/LLMClient.java b/MathCaptain/weakness/src/main/java/MathCaptain/weakness/domain/Chat/service/LLMClient.java new file mode 100644 index 0000000..dbbd4e2 --- /dev/null +++ b/MathCaptain/weakness/src/main/java/MathCaptain/weakness/domain/Chat/service/LLMClient.java @@ -0,0 +1,63 @@ +package MathCaptain.weakness.domain.Chat.service; + +import MathCaptain.weakness.domain.Chat.dto.request.ChatRequest; +import MathCaptain.weakness.domain.Chat.dto.request.LLMRequest; +import MathCaptain.weakness.domain.Chat.dto.response.ChatResponse; +import MathCaptain.weakness.domain.Chat.entity.Chat; +import MathCaptain.weakness.domain.User.entity.Users; +import groovy.util.logging.Slf4j; +import lombok.RequiredArgsConstructor; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.http.ResponseEntity; +import org.springframework.stereotype.Component; +import org.springframework.web.client.HttpClientErrorException; +import org.springframework.web.client.HttpServerErrorException; +import org.springframework.web.client.ResourceAccessException; +import org.springframework.web.client.RestTemplate; + +import java.util.Arrays; +import java.util.Collections; +import java.util.List; +import java.util.stream.Collectors; + +@lombok.extern.slf4j.Slf4j +@Slf4j +@Component +@RequiredArgsConstructor +public class LLMClient { + + private final RestTemplate restTemplate; + + @Value("${llm.server.url}") + private String baseUrl; + + public List call(Users loginUser, List history, ChatRequest request) { + try { + LLMRequest llmRequest = LLMRequest.of(loginUser, request, history); + + ResponseEntity response = restTemplate.postForEntity( + // TODO : 엔드포인트 수정 + baseUrl + "/v1/chat", + llmRequest, + // 응답을 ChatResponse[] (배열)로 역직렬화 (수정 필요) + ChatResponse[].class + ); + + ChatResponse[] body = response.getBody(); + if (body == null) { + return Collections.emptyList(); + } + return Arrays.stream(body) + .map(Chat::of) + .collect(Collectors.toList()); + + } catch (ResourceAccessException timeoutEx) { + log.error("LLM 서버 응답 지연", timeoutEx); + return List.of(); + } catch (HttpClientErrorException | HttpServerErrorException httpEx) { + // 4XX/5XX 응답 + log.error("LLM 호출 실패: {}", httpEx.getStatusCode(), httpEx); + return List.of(); + } + } +} \ No newline at end of file diff --git a/MathCaptain/weakness/src/main/java/MathCaptain/weakness/domain/Group/controller/GroupJoinController.java b/MathCaptain/weakness/src/main/java/MathCaptain/weakness/domain/Group/controller/GroupJoinController.java index 7576666..8504b11 100644 --- a/MathCaptain/weakness/src/main/java/MathCaptain/weakness/domain/Group/controller/GroupJoinController.java +++ b/MathCaptain/weakness/src/main/java/MathCaptain/weakness/domain/Group/controller/GroupJoinController.java @@ -1,7 +1,7 @@ package MathCaptain.weakness.domain.Group.controller; import MathCaptain.weakness.domain.Group.dto.request.GroupJoinRequest; -import MathCaptain.weakness.domain.Group.enums.RequestStatus; +import MathCaptain.weakness.domain.common.enums.RequestStatus; import MathCaptain.weakness.domain.Group.service.GroupJoinService; import MathCaptain.weakness.domain.Group.service.RelationService; import MathCaptain.weakness.domain.Notification.service.NotificationService; diff --git a/MathCaptain/weakness/src/main/java/MathCaptain/weakness/domain/Group/dto/request/GroupCreateRequest.java b/MathCaptain/weakness/src/main/java/MathCaptain/weakness/domain/Group/dto/request/GroupCreateRequest.java index fef028d..76ab53f 100644 --- a/MathCaptain/weakness/src/main/java/MathCaptain/weakness/domain/Group/dto/request/GroupCreateRequest.java +++ b/MathCaptain/weakness/src/main/java/MathCaptain/weakness/domain/Group/dto/request/GroupCreateRequest.java @@ -1,6 +1,6 @@ package MathCaptain.weakness.domain.Group.dto.request; -import MathCaptain.weakness.domain.Group.enums.CategoryStatus; +import MathCaptain.weakness.domain.common.enums.CategoryStatus; import jakarta.validation.constraints.NotNull; import jakarta.validation.constraints.Size; import lombok.*; diff --git a/MathCaptain/weakness/src/main/java/MathCaptain/weakness/domain/Group/dto/response/GroupDetailResponse.java b/MathCaptain/weakness/src/main/java/MathCaptain/weakness/domain/Group/dto/response/GroupDetailResponse.java index b6d7090..101be11 100644 --- a/MathCaptain/weakness/src/main/java/MathCaptain/weakness/domain/Group/dto/response/GroupDetailResponse.java +++ b/MathCaptain/weakness/src/main/java/MathCaptain/weakness/domain/Group/dto/response/GroupDetailResponse.java @@ -1,7 +1,7 @@ package MathCaptain.weakness.domain.Group.dto.response; import MathCaptain.weakness.domain.Group.entity.Group; -import MathCaptain.weakness.domain.Group.enums.CategoryStatus; +import MathCaptain.weakness.domain.common.enums.CategoryStatus; import MathCaptain.weakness.domain.User.entity.Users; import lombok.*; diff --git a/MathCaptain/weakness/src/main/java/MathCaptain/weakness/domain/Group/dto/response/GroupMemberListResponse.java b/MathCaptain/weakness/src/main/java/MathCaptain/weakness/domain/Group/dto/response/GroupMemberListResponse.java index b02f22a..ef53853 100644 --- a/MathCaptain/weakness/src/main/java/MathCaptain/weakness/domain/Group/dto/response/GroupMemberListResponse.java +++ b/MathCaptain/weakness/src/main/java/MathCaptain/weakness/domain/Group/dto/response/GroupMemberListResponse.java @@ -1,7 +1,7 @@ package MathCaptain.weakness.domain.Group.dto.response; import MathCaptain.weakness.domain.Group.entity.RelationBetweenUserAndGroup; -import MathCaptain.weakness.domain.Group.enums.GroupRole; +import MathCaptain.weakness.domain.common.enums.GroupRole; import jakarta.validation.constraints.NotNull; import lombok.*; diff --git a/MathCaptain/weakness/src/main/java/MathCaptain/weakness/domain/Group/dto/response/GroupResponse.java b/MathCaptain/weakness/src/main/java/MathCaptain/weakness/domain/Group/dto/response/GroupResponse.java index 6f2f902..07f173d 100644 --- a/MathCaptain/weakness/src/main/java/MathCaptain/weakness/domain/Group/dto/response/GroupResponse.java +++ b/MathCaptain/weakness/src/main/java/MathCaptain/weakness/domain/Group/dto/response/GroupResponse.java @@ -2,7 +2,7 @@ import MathCaptain.weakness.domain.Group.entity.Group; import MathCaptain.weakness.domain.Group.entity.RelationBetweenUserAndGroup; -import MathCaptain.weakness.domain.Group.enums.CategoryStatus; +import MathCaptain.weakness.domain.common.enums.CategoryStatus; import MathCaptain.weakness.domain.User.entity.Users; import lombok.*; diff --git a/MathCaptain/weakness/src/main/java/MathCaptain/weakness/domain/Group/dto/response/RelationResponse.java b/MathCaptain/weakness/src/main/java/MathCaptain/weakness/domain/Group/dto/response/RelationResponse.java index ae29c67..67b35d4 100644 --- a/MathCaptain/weakness/src/main/java/MathCaptain/weakness/domain/Group/dto/response/RelationResponse.java +++ b/MathCaptain/weakness/src/main/java/MathCaptain/weakness/domain/Group/dto/response/RelationResponse.java @@ -1,7 +1,7 @@ package MathCaptain.weakness.domain.Group.dto.response; import MathCaptain.weakness.domain.Group.entity.RelationBetweenUserAndGroup; -import MathCaptain.weakness.domain.Group.enums.GroupRole; +import MathCaptain.weakness.domain.common.enums.GroupRole; import MathCaptain.weakness.domain.User.dto.response.UserResponse; import lombok.*; diff --git a/MathCaptain/weakness/src/main/java/MathCaptain/weakness/domain/Group/dto/response/UserGroupCardResponse.java b/MathCaptain/weakness/src/main/java/MathCaptain/weakness/domain/Group/dto/response/UserGroupCardResponse.java index 2100441..d5a3bcb 100644 --- a/MathCaptain/weakness/src/main/java/MathCaptain/weakness/domain/Group/dto/response/UserGroupCardResponse.java +++ b/MathCaptain/weakness/src/main/java/MathCaptain/weakness/domain/Group/dto/response/UserGroupCardResponse.java @@ -2,7 +2,7 @@ import MathCaptain.weakness.domain.Group.entity.Group; import MathCaptain.weakness.domain.Group.entity.RelationBetweenUserAndGroup; -import MathCaptain.weakness.domain.Group.enums.GroupRole; +import MathCaptain.weakness.domain.common.enums.GroupRole; import lombok.*; import java.time.DayOfWeek; diff --git a/MathCaptain/weakness/src/main/java/MathCaptain/weakness/domain/Group/entity/Group.java b/MathCaptain/weakness/src/main/java/MathCaptain/weakness/domain/Group/entity/Group.java index 9eadf67..d798217 100644 --- a/MathCaptain/weakness/src/main/java/MathCaptain/weakness/domain/Group/entity/Group.java +++ b/MathCaptain/weakness/src/main/java/MathCaptain/weakness/domain/Group/entity/Group.java @@ -2,7 +2,7 @@ import MathCaptain.weakness.domain.Group.dto.request.GroupCreateRequest; import MathCaptain.weakness.domain.Group.dto.request.GroupUpdateRequest; -import MathCaptain.weakness.domain.Group.enums.CategoryStatus; +import MathCaptain.weakness.domain.common.enums.CategoryStatus; import MathCaptain.weakness.domain.Recruitment.entity.Recruitment; import jakarta.persistence.*; import lombok.*; diff --git a/MathCaptain/weakness/src/main/java/MathCaptain/weakness/domain/Group/entity/RelationBetweenUserAndGroup.java b/MathCaptain/weakness/src/main/java/MathCaptain/weakness/domain/Group/entity/RelationBetweenUserAndGroup.java index de68e28..ae6150d 100644 --- a/MathCaptain/weakness/src/main/java/MathCaptain/weakness/domain/Group/entity/RelationBetweenUserAndGroup.java +++ b/MathCaptain/weakness/src/main/java/MathCaptain/weakness/domain/Group/entity/RelationBetweenUserAndGroup.java @@ -2,8 +2,8 @@ import MathCaptain.weakness.domain.Group.dto.request.GroupCreateRequest; import MathCaptain.weakness.domain.Group.dto.request.GroupJoinRequest; -import MathCaptain.weakness.domain.Group.enums.GroupRole; -import MathCaptain.weakness.domain.Group.enums.RequestStatus; +import MathCaptain.weakness.domain.common.enums.GroupRole; +import MathCaptain.weakness.domain.common.enums.RequestStatus; import MathCaptain.weakness.domain.User.entity.Users; import jakarta.persistence.*; import lombok.*; diff --git a/MathCaptain/weakness/src/main/java/MathCaptain/weakness/domain/Group/repository/GroupRepository.java b/MathCaptain/weakness/src/main/java/MathCaptain/weakness/domain/Group/repository/GroupRepository.java index 9715b9a..c0f3215 100644 --- a/MathCaptain/weakness/src/main/java/MathCaptain/weakness/domain/Group/repository/GroupRepository.java +++ b/MathCaptain/weakness/src/main/java/MathCaptain/weakness/domain/Group/repository/GroupRepository.java @@ -1,7 +1,7 @@ package MathCaptain.weakness.domain.Group.repository; import MathCaptain.weakness.domain.Group.entity.Group; -import MathCaptain.weakness.domain.Group.enums.CategoryStatus; +import MathCaptain.weakness.domain.common.enums.CategoryStatus; import org.springframework.data.domain.Page; import org.springframework.data.domain.Pageable; import org.springframework.data.jpa.repository.JpaRepository; diff --git a/MathCaptain/weakness/src/main/java/MathCaptain/weakness/domain/Group/repository/RelationRepository.java b/MathCaptain/weakness/src/main/java/MathCaptain/weakness/domain/Group/repository/RelationRepository.java index f29fb76..588bad0 100644 --- a/MathCaptain/weakness/src/main/java/MathCaptain/weakness/domain/Group/repository/RelationRepository.java +++ b/MathCaptain/weakness/src/main/java/MathCaptain/weakness/domain/Group/repository/RelationRepository.java @@ -2,8 +2,8 @@ import MathCaptain.weakness.domain.Group.entity.Group; import MathCaptain.weakness.domain.Group.entity.RelationBetweenUserAndGroup; -import MathCaptain.weakness.domain.Group.enums.GroupRole; -import MathCaptain.weakness.domain.Group.enums.RequestStatus; +import MathCaptain.weakness.domain.common.enums.GroupRole; +import MathCaptain.weakness.domain.common.enums.RequestStatus; import MathCaptain.weakness.domain.User.entity.Users; import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.data.jpa.repository.Query; diff --git a/MathCaptain/weakness/src/main/java/MathCaptain/weakness/domain/Group/service/GroupJoinService.java b/MathCaptain/weakness/src/main/java/MathCaptain/weakness/domain/Group/service/GroupJoinService.java index 6909e5a..93608ad 100644 --- a/MathCaptain/weakness/src/main/java/MathCaptain/weakness/domain/Group/service/GroupJoinService.java +++ b/MathCaptain/weakness/src/main/java/MathCaptain/weakness/domain/Group/service/GroupJoinService.java @@ -4,7 +4,7 @@ import MathCaptain.weakness.domain.Group.entity.RelationBetweenUserAndGroup; import MathCaptain.weakness.domain.Group.dto.request.GroupJoinRequest; import MathCaptain.weakness.domain.Group.dto.response.GroupJoinResponse; -import MathCaptain.weakness.domain.Group.enums.RequestStatus; +import MathCaptain.weakness.domain.common.enums.RequestStatus; import MathCaptain.weakness.domain.Group.repository.GroupRepository; import MathCaptain.weakness.domain.Group.repository.RelationRepository; import MathCaptain.weakness.domain.User.entity.Users; diff --git a/MathCaptain/weakness/src/main/java/MathCaptain/weakness/domain/Group/service/GroupService.java b/MathCaptain/weakness/src/main/java/MathCaptain/weakness/domain/Group/service/GroupService.java index 00b601b..eb7865e 100644 --- a/MathCaptain/weakness/src/main/java/MathCaptain/weakness/domain/Group/service/GroupService.java +++ b/MathCaptain/weakness/src/main/java/MathCaptain/weakness/domain/Group/service/GroupService.java @@ -7,7 +7,7 @@ import MathCaptain.weakness.domain.Group.dto.response.GroupDetailResponse; import MathCaptain.weakness.domain.Group.dto.response.GroupResponse; import MathCaptain.weakness.domain.Group.dto.response.UserGroupCardResponse; -import MathCaptain.weakness.domain.Group.enums.CategoryStatus; +import MathCaptain.weakness.domain.common.enums.CategoryStatus; import MathCaptain.weakness.domain.Record.service.RecordService; import MathCaptain.weakness.domain.User.dto.response.UserResponse; import MathCaptain.weakness.domain.Group.repository.GroupRepository; @@ -15,7 +15,6 @@ import MathCaptain.weakness.domain.User.entity.Users; import MathCaptain.weakness.global.Api.ApiResponse; import MathCaptain.weakness.global.exception.ResourceNotFoundException; -import jakarta.servlet.http.HttpServletResponse; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Service; diff --git a/MathCaptain/weakness/src/main/java/MathCaptain/weakness/domain/Group/service/RelationService.java b/MathCaptain/weakness/src/main/java/MathCaptain/weakness/domain/Group/service/RelationService.java index 0a66c06..acdc46f 100644 --- a/MathCaptain/weakness/src/main/java/MathCaptain/weakness/domain/Group/service/RelationService.java +++ b/MathCaptain/weakness/src/main/java/MathCaptain/weakness/domain/Group/service/RelationService.java @@ -8,7 +8,7 @@ import MathCaptain.weakness.domain.Group.repository.GroupRepository; import MathCaptain.weakness.domain.Record.repository.record.RecordRepository; import MathCaptain.weakness.domain.User.dto.response.UserResponse; -import MathCaptain.weakness.domain.Group.enums.GroupRole; +import MathCaptain.weakness.domain.common.enums.GroupRole; import MathCaptain.weakness.domain.Group.repository.RelationRepository; import MathCaptain.weakness.domain.User.entity.Users; import MathCaptain.weakness.global.Api.ApiResponse; diff --git a/MathCaptain/weakness/src/main/java/MathCaptain/weakness/domain/Notification/controller/NotificationController.java b/MathCaptain/weakness/src/main/java/MathCaptain/weakness/domain/Notification/controller/NotificationController.java index ae8c79e..c503dc0 100644 --- a/MathCaptain/weakness/src/main/java/MathCaptain/weakness/domain/Notification/controller/NotificationController.java +++ b/MathCaptain/weakness/src/main/java/MathCaptain/weakness/domain/Notification/controller/NotificationController.java @@ -23,11 +23,16 @@ public class NotificationController { @GetMapping("/notification/subscribe") public SseEmitter subscribe(@LoginUser Users loginUser) { - return notificationService.subscribe(loginUser.getUserId()); + return notificationService.subscribe(loginUser); } @DeleteMapping("/notification/delete/{notificationId}") public ApiResponse deleteNotification(@LoginUser Users loginUser, @PathVariable Long notificationId) { return notificationService.deleteNotification(loginUser, notificationId); } -} + + @GetMapping("/notification/list") + public ApiResponse getNotificationList(@LoginUser Users loginUser) { + return notificationService.getNotificationList(loginUser); + } +} \ No newline at end of file diff --git a/MathCaptain/weakness/src/main/java/MathCaptain/weakness/domain/Notification/dto/response/NotificationResponse.java b/MathCaptain/weakness/src/main/java/MathCaptain/weakness/domain/Notification/dto/response/NotificationResponse.java new file mode 100644 index 0000000..8a33002 --- /dev/null +++ b/MathCaptain/weakness/src/main/java/MathCaptain/weakness/domain/Notification/dto/response/NotificationResponse.java @@ -0,0 +1,46 @@ +package MathCaptain.weakness.domain.Notification.dto.response; + +import MathCaptain.weakness.domain.Notification.entity.Notification; +import lombok.Builder; +import lombok.Getter; +import lombok.NoArgsConstructor; + +import java.time.LocalDateTime; +import java.util.List; +import java.util.stream.Collectors; + +@Getter +@NoArgsConstructor +public class NotificationResponse { + + private Long id; + private String sender; + private String content; + private LocalDateTime createdAt; + private Boolean isRead; + + @Builder + public NotificationResponse(Long id, String sender, String content, LocalDateTime createdAt, Boolean isRead) { + this.id = id; + this.sender = sender; + this.content = content; + this.createdAt = createdAt; + this.isRead = isRead; + } + + public static NotificationResponse from(Notification notification) { + return NotificationResponse.builder() + .id(notification.getId()) + .sender(notification.getSender()) + .content(notification.getContent()) + .createdAt(notification.getCreatedAt()) + .isRead(notification.getIsRead()) + .build(); + } + + public static List fromList(List notifications) { + return notifications.stream() + .map(NotificationResponse::from) + .collect(Collectors.toList()); + } +} \ No newline at end of file diff --git a/MathCaptain/weakness/src/main/java/MathCaptain/weakness/domain/Notification/entity/Notification.java b/MathCaptain/weakness/src/main/java/MathCaptain/weakness/domain/Notification/entity/Notification.java index 641e9b9..a0bc3db 100644 --- a/MathCaptain/weakness/src/main/java/MathCaptain/weakness/domain/Notification/entity/Notification.java +++ b/MathCaptain/weakness/src/main/java/MathCaptain/weakness/domain/Notification/entity/Notification.java @@ -18,6 +18,8 @@ public class Notification { @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; + private Long userId; + @Column(nullable = false) private String sender; @@ -25,20 +27,24 @@ public class Notification { private LocalDateTime createdAt; @Column(nullable = false) - private String contents; + private String content; + + private Boolean isRead; @Builder - private Notification(String sender, LocalDateTime createdAt, String contents) { + private Notification(Long userId, String sender, LocalDateTime createdAt, String content) { + this.userId = userId; this.sender = sender; this.createdAt = createdAt; - this.contents = contents; + this.content = content; + this.isRead = false; } public static Notification of(Map eventData) { return Notification.builder() + .userId(Long.parseLong(eventData.get("userId"))) .sender(eventData.get("sender")) - .createdAt(LocalDateTime.parse(eventData.get("createdAt"))) - .contents(eventData.get("message")) + .content(eventData.get("message")) .build(); } } diff --git a/MathCaptain/weakness/src/main/java/MathCaptain/weakness/domain/Notification/repository/NotificationRepository.java b/MathCaptain/weakness/src/main/java/MathCaptain/weakness/domain/Notification/repository/NotificationRepository.java index c107b2c..4f0020e 100644 --- a/MathCaptain/weakness/src/main/java/MathCaptain/weakness/domain/Notification/repository/NotificationRepository.java +++ b/MathCaptain/weakness/src/main/java/MathCaptain/weakness/domain/Notification/repository/NotificationRepository.java @@ -3,8 +3,11 @@ import MathCaptain.weakness.domain.Notification.entity.Notification; import org.springframework.data.jpa.repository.JpaRepository; +import java.util.List; import java.util.Optional; public interface NotificationRepository extends JpaRepository { Optional findById(Long id); + + List findAllByUserIdOrderByCreatedAtDesc(Long userId); } diff --git a/MathCaptain/weakness/src/main/java/MathCaptain/weakness/domain/Notification/service/NotificationService.java b/MathCaptain/weakness/src/main/java/MathCaptain/weakness/domain/Notification/service/NotificationService.java index 8b373c1..99566f2 100644 --- a/MathCaptain/weakness/src/main/java/MathCaptain/weakness/domain/Notification/service/NotificationService.java +++ b/MathCaptain/weakness/src/main/java/MathCaptain/weakness/domain/Notification/service/NotificationService.java @@ -5,6 +5,7 @@ import MathCaptain.weakness.domain.Group.repository.GroupRepository; import MathCaptain.weakness.domain.Group.repository.RelationRepository; import MathCaptain.weakness.domain.Notification.controller.NotificationController; +import MathCaptain.weakness.domain.Notification.dto.response.NotificationResponse; import MathCaptain.weakness.domain.Notification.entity.Notification; import MathCaptain.weakness.domain.Notification.repository.NotificationRepository; import MathCaptain.weakness.domain.Recruitment.entity.Comment; @@ -20,6 +21,7 @@ import java.io.IOException; import java.time.LocalDateTime; import java.util.HashMap; +import java.util.List; import java.util.Map; @Service @@ -36,7 +38,8 @@ public class NotificationService { // 메시지 알림 - public SseEmitter subscribe(Long userId) { + public SseEmitter subscribe(Users user) { + Long userId = user.getUserId(); SseEmitter sseEmitter = new SseEmitter(Long.MAX_VALUE); try { sseEmitter.send(SseEmitter.event().name("connect")); @@ -60,7 +63,7 @@ public void notifyComment(Long recruitmentId, Long commentId) { if (NotificationController.sseEmitters.containsKey(userId)) { SseEmitter sseEmitter = NotificationController.sseEmitters.get(userId); try { - Map eventData = buildNotificationData("댓글이 달렸습니다.", comment.getAuthor().getNickname(), comment.getCommentTime().toString(), comment.getContent()); + Map eventData = buildNotificationData("댓글이 달렸습니다.", comment.getAuthor().getUserId(), comment.getAuthor().getNickname(), comment.getCommentTime().toString(), comment.getContent()); sseEmitter.send(SseEmitter.event().name("addComment").data(eventData)); Notification notification = Notification.of(eventData); notificationRepository.save(notification); @@ -83,7 +86,7 @@ public void notifyGroupJoinRequest(Long groupId, Users user) { if (NotificationController.sseEmitters.containsKey(leaderId)) { SseEmitter sseEmitter = NotificationController.sseEmitters.get(leaderId); try { - Map eventData = buildNotificationData("그룹 가입 요청이 있습니다.", user.getNickname(), LocalDateTime.now().toString(), "가입 요청"); + Map eventData = buildNotificationData("그룹 가입 요청이 있습니다.", leaderId ,user.getNickname(), LocalDateTime.now().toString(), "가입 요청"); sseEmitter.send(SseEmitter.event().name("groupJoinRequest").data(eventData)); // DB 저장 Notification notification = Notification.of(eventData); @@ -107,7 +110,7 @@ public void notifyGroupJoinResult(Long groupId, Users user) { if (NotificationController.sseEmitters.containsKey(userId)) { SseEmitter sseEmitter = NotificationController.sseEmitters.get(userId); try { - Map eventData = buildNotificationData("그룹 가입 요청 결과가 도착했습니다.", group.getName(), LocalDateTime.now().toString(), relation.getRequestStatus().toString()); + Map eventData = buildNotificationData("그룹 가입 요청 결과가 도착했습니다.", userId ,group.getName(), LocalDateTime.now().toString(), relation.getRequestStatus().toString()); sseEmitter.send(SseEmitter.event().name("groupJoinResult").data(eventData)); // DB 저장 Notification notification = Notification.of(eventData); @@ -144,8 +147,16 @@ public ApiResponse deleteNotification(Users loginUser, Long notificationId) { return ApiResponse.ok("알림이 삭제되었습니다."); } - private Map buildNotificationData(String message, String sender, String createdAt, String contents) { + public ApiResponse getNotificationList(Users user) { + Long userId = user.getUserId(); + List notifications = notificationRepository.findAllByUserIdOrderByCreatedAtDesc(userId); + return ApiResponse.ok(NotificationResponse.fromList(notifications)); + } + + private Map buildNotificationData(String message, Long userId, String sender, String createdAt, String contents) { Map eventData = new HashMap<>(); + eventData.put("userId", userId.toString()); + eventData.put("userName", String.valueOf(sender)); eventData.put("message", message); eventData.put("sender", sender); eventData.put("createdAt", createdAt); diff --git a/MathCaptain/weakness/src/main/java/MathCaptain/weakness/domain/Record/controller/RecordController.java b/MathCaptain/weakness/src/main/java/MathCaptain/weakness/domain/Record/controller/RecordController.java index b64b2a4..2a909a5 100644 --- a/MathCaptain/weakness/src/main/java/MathCaptain/weakness/domain/Record/controller/RecordController.java +++ b/MathCaptain/weakness/src/main/java/MathCaptain/weakness/domain/Record/controller/RecordController.java @@ -1,7 +1,6 @@ package MathCaptain.weakness.domain.Record.controller; import MathCaptain.weakness.domain.Record.dto.request.FitnessLogEnrollRequest; -import MathCaptain.weakness.domain.Record.dto.request.RecordEndRequest; import MathCaptain.weakness.domain.Record.dto.request.RunningLogEnrollRequest; import MathCaptain.weakness.domain.Record.dto.request.StudyLogEnrollRequest; import MathCaptain.weakness.domain.Record.dto.response.RecordSummaryResponse; @@ -13,7 +12,7 @@ import lombok.RequiredArgsConstructor; import org.springframework.web.bind.annotation.*; -import static MathCaptain.weakness.domain.Group.enums.CategoryStatus.*; +import static MathCaptain.weakness.domain.common.enums.CategoryStatus.*; @RestController @RequiredArgsConstructor diff --git a/MathCaptain/weakness/src/main/java/MathCaptain/weakness/domain/Record/service/RecordService.java b/MathCaptain/weakness/src/main/java/MathCaptain/weakness/domain/Record/service/RecordService.java index 3b9c520..3812f77 100644 --- a/MathCaptain/weakness/src/main/java/MathCaptain/weakness/domain/Record/service/RecordService.java +++ b/MathCaptain/weakness/src/main/java/MathCaptain/weakness/domain/Record/service/RecordService.java @@ -2,7 +2,7 @@ import MathCaptain.weakness.domain.Group.entity.Group; import MathCaptain.weakness.domain.Group.entity.RelationBetweenUserAndGroup; -import MathCaptain.weakness.domain.Group.enums.CategoryStatus; +import MathCaptain.weakness.domain.common.enums.CategoryStatus; import MathCaptain.weakness.domain.Group.repository.RelationRepository; import MathCaptain.weakness.domain.Record.dto.request.*; import MathCaptain.weakness.domain.Record.dto.response.FitnessLogResponse; @@ -10,9 +10,6 @@ import MathCaptain.weakness.domain.Record.dto.response.StudyLogResponse; import MathCaptain.weakness.domain.Record.entity.ActivityRecord; import MathCaptain.weakness.domain.Record.dto.response.RecordSummaryResponse; -import MathCaptain.weakness.domain.Record.entity.UserLog.FitnessDetail; -import MathCaptain.weakness.domain.Record.entity.UserLog.RunningDetail; -import MathCaptain.weakness.domain.Record.entity.UserLog.StudyDetail; import MathCaptain.weakness.domain.Record.repository.record.RecordRepository; import MathCaptain.weakness.domain.User.entity.Users; import MathCaptain.weakness.global.PointSet; @@ -22,14 +19,11 @@ import org.springframework.transaction.annotation.Transactional; import java.time.DayOfWeek; -import java.time.LocalDate; import java.time.LocalDateTime; import java.time.temporal.TemporalAdjusters; import java.util.*; import java.util.stream.Collectors; -import static MathCaptain.weakness.domain.Group.enums.CategoryStatus.*; - @Slf4j @Service @Transactional diff --git a/MathCaptain/weakness/src/main/java/MathCaptain/weakness/domain/Recruitment/dto/request/UpdateRecruitmentRequest.java b/MathCaptain/weakness/src/main/java/MathCaptain/weakness/domain/Recruitment/dto/request/UpdateRecruitmentRequest.java index 459fc97..04d9401 100644 --- a/MathCaptain/weakness/src/main/java/MathCaptain/weakness/domain/Recruitment/dto/request/UpdateRecruitmentRequest.java +++ b/MathCaptain/weakness/src/main/java/MathCaptain/weakness/domain/Recruitment/dto/request/UpdateRecruitmentRequest.java @@ -1,6 +1,6 @@ package MathCaptain.weakness.domain.Recruitment.dto.request; -import MathCaptain.weakness.domain.Recruitment.enums.RecruitmentStatus; +import MathCaptain.weakness.domain.common.enums.RecruitmentStatus; import jakarta.validation.constraints.NotEmpty; import jakarta.validation.constraints.NotNull; import jakarta.validation.constraints.Size; diff --git a/MathCaptain/weakness/src/main/java/MathCaptain/weakness/domain/Recruitment/dto/response/RecruitmentDetailResponse.java b/MathCaptain/weakness/src/main/java/MathCaptain/weakness/domain/Recruitment/dto/response/RecruitmentDetailResponse.java index 2459ca3..61cd81e 100644 --- a/MathCaptain/weakness/src/main/java/MathCaptain/weakness/domain/Recruitment/dto/response/RecruitmentDetailResponse.java +++ b/MathCaptain/weakness/src/main/java/MathCaptain/weakness/domain/Recruitment/dto/response/RecruitmentDetailResponse.java @@ -1,8 +1,8 @@ package MathCaptain.weakness.domain.Recruitment.dto.response; -import MathCaptain.weakness.domain.Group.enums.CategoryStatus; +import MathCaptain.weakness.domain.common.enums.CategoryStatus; import MathCaptain.weakness.domain.Recruitment.entity.Recruitment; -import MathCaptain.weakness.domain.Recruitment.enums.RecruitmentStatus; +import MathCaptain.weakness.domain.common.enums.RecruitmentStatus; import MathCaptain.weakness.domain.User.entity.Users; import lombok.*; diff --git a/MathCaptain/weakness/src/main/java/MathCaptain/weakness/domain/Recruitment/dto/response/RecruitmentResponse.java b/MathCaptain/weakness/src/main/java/MathCaptain/weakness/domain/Recruitment/dto/response/RecruitmentResponse.java index 6f4bea7..5ab396c 100644 --- a/MathCaptain/weakness/src/main/java/MathCaptain/weakness/domain/Recruitment/dto/response/RecruitmentResponse.java +++ b/MathCaptain/weakness/src/main/java/MathCaptain/weakness/domain/Recruitment/dto/response/RecruitmentResponse.java @@ -1,8 +1,8 @@ package MathCaptain.weakness.domain.Recruitment.dto.response; -import MathCaptain.weakness.domain.Group.enums.CategoryStatus; +import MathCaptain.weakness.domain.common.enums.CategoryStatus; import MathCaptain.weakness.domain.Recruitment.entity.Recruitment; -import MathCaptain.weakness.domain.Recruitment.enums.RecruitmentStatus; +import MathCaptain.weakness.domain.common.enums.RecruitmentStatus; import lombok.*; import java.time.LocalDateTime; diff --git a/MathCaptain/weakness/src/main/java/MathCaptain/weakness/domain/Recruitment/entity/Recruitment.java b/MathCaptain/weakness/src/main/java/MathCaptain/weakness/domain/Recruitment/entity/Recruitment.java index 8f907b4..a7c35c9 100644 --- a/MathCaptain/weakness/src/main/java/MathCaptain/weakness/domain/Recruitment/entity/Recruitment.java +++ b/MathCaptain/weakness/src/main/java/MathCaptain/weakness/domain/Recruitment/entity/Recruitment.java @@ -1,10 +1,10 @@ package MathCaptain.weakness.domain.Recruitment.entity; import MathCaptain.weakness.domain.Group.entity.Group; -import MathCaptain.weakness.domain.Group.enums.CategoryStatus; +import MathCaptain.weakness.domain.common.enums.CategoryStatus; import MathCaptain.weakness.domain.Recruitment.dto.request.CreateRecruitmentRequest; import MathCaptain.weakness.domain.Recruitment.dto.request.UpdateRecruitmentRequest; -import MathCaptain.weakness.domain.Recruitment.enums.RecruitmentStatus; +import MathCaptain.weakness.domain.common.enums.RecruitmentStatus; import MathCaptain.weakness.domain.User.entity.Users; import jakarta.persistence.*; import jakarta.validation.constraints.NotNull; diff --git a/MathCaptain/weakness/src/main/java/MathCaptain/weakness/domain/Recruitment/service/RecruitmentService.java b/MathCaptain/weakness/src/main/java/MathCaptain/weakness/domain/Recruitment/service/RecruitmentService.java index 343815b..f1e9c1d 100644 --- a/MathCaptain/weakness/src/main/java/MathCaptain/weakness/domain/Recruitment/service/RecruitmentService.java +++ b/MathCaptain/weakness/src/main/java/MathCaptain/weakness/domain/Recruitment/service/RecruitmentService.java @@ -2,7 +2,7 @@ import MathCaptain.weakness.domain.Group.entity.Group; import MathCaptain.weakness.domain.Group.entity.RelationBetweenUserAndGroup; -import MathCaptain.weakness.domain.Group.enums.GroupRole; +import MathCaptain.weakness.domain.common.enums.GroupRole; import MathCaptain.weakness.domain.Group.repository.GroupRepository; import MathCaptain.weakness.domain.Group.repository.RelationRepository; import MathCaptain.weakness.domain.Recruitment.dto.response.*; @@ -19,7 +19,6 @@ import org.springframework.transaction.annotation.Transactional; import java.util.List; -import java.util.Optional; @Service @Slf4j diff --git a/MathCaptain/weakness/src/main/java/MathCaptain/weakness/domain/User/Login/LoginSuccessJWTProvideHandler.java b/MathCaptain/weakness/src/main/java/MathCaptain/weakness/domain/User/Login/LoginSuccessJWTProvideHandler.java index cf93c87..fed79ce 100644 --- a/MathCaptain/weakness/src/main/java/MathCaptain/weakness/domain/User/Login/LoginSuccessJWTProvideHandler.java +++ b/MathCaptain/weakness/src/main/java/MathCaptain/weakness/domain/User/Login/LoginSuccessJWTProvideHandler.java @@ -16,6 +16,7 @@ import org.springframework.security.web.authentication.SimpleUrlAuthenticationSuccessHandler; import java.io.IOException; +import java.util.Map; @Slf4j @RequiredArgsConstructor @@ -40,13 +41,28 @@ public void onAuthenticationSuccess(HttpServletRequest request, HttpServletRespo user -> user.updateRefreshToken(refreshToken) ); - // TODO - // 사용자의 그룹 정보 조회를 리다이렉트를 이용하여 옮기는 것이 좋아보임 + userRepository.findByEmail(email).ifPresent(user -> { + try { + var responseBody = objectMapper.writeValueAsString(Map.of( + "status", true, + "message", "로그인 성공", + "data", Map.of( + "userId", user.getUserId(), + "email", email + ) + )); - // JSON 응답 설정 및 전송 - response.setContentType("application/json"); - response.setCharacterEncoding("UTF-8"); - response.setStatus(HttpServletResponse.SC_OK); + // JSON 응답 설정 및 전송 + response.setContentType("application/json"); + response.setCharacterEncoding("UTF-8"); + response.setStatus(HttpServletResponse.SC_OK); + response.getWriter().write(responseBody); + + } catch (IOException e) { + log.error("❌JSON 응답 실패", e); + throw new RuntimeException(e); + } + }); log.info( "✅ 로그인에 성공합니다. 📧email: {}" , email); } diff --git a/MathCaptain/weakness/src/main/java/MathCaptain/weakness/domain/User/dto/response/UserCardResponse.java b/MathCaptain/weakness/src/main/java/MathCaptain/weakness/domain/User/dto/response/UserCardResponse.java index 46011a4..ed0f170 100644 --- a/MathCaptain/weakness/src/main/java/MathCaptain/weakness/domain/User/dto/response/UserCardResponse.java +++ b/MathCaptain/weakness/src/main/java/MathCaptain/weakness/domain/User/dto/response/UserCardResponse.java @@ -2,7 +2,7 @@ import MathCaptain.weakness.domain.Group.dto.response.UserGroupCardResponse; import MathCaptain.weakness.domain.User.entity.Users; -import MathCaptain.weakness.domain.User.enums.Tiers; +import MathCaptain.weakness.domain.common.enums.Tiers; import lombok.*; import java.util.List; diff --git a/MathCaptain/weakness/src/main/java/MathCaptain/weakness/domain/User/dto/response/UserResponse.java b/MathCaptain/weakness/src/main/java/MathCaptain/weakness/domain/User/dto/response/UserResponse.java index 3bdcefd..3d42e3f 100644 --- a/MathCaptain/weakness/src/main/java/MathCaptain/weakness/domain/User/dto/response/UserResponse.java +++ b/MathCaptain/weakness/src/main/java/MathCaptain/weakness/domain/User/dto/response/UserResponse.java @@ -3,7 +3,7 @@ import MathCaptain.weakness.domain.Group.dto.response.GroupResponse; import MathCaptain.weakness.domain.Group.entity.RelationBetweenUserAndGroup; import MathCaptain.weakness.domain.User.entity.Users; -import MathCaptain.weakness.domain.User.enums.Tiers; +import MathCaptain.weakness.domain.common.enums.Tiers; import lombok.*; import java.util.List; diff --git a/MathCaptain/weakness/src/main/java/MathCaptain/weakness/domain/User/entity/Users.java b/MathCaptain/weakness/src/main/java/MathCaptain/weakness/domain/User/entity/Users.java index 9885555..891b416 100644 --- a/MathCaptain/weakness/src/main/java/MathCaptain/weakness/domain/User/entity/Users.java +++ b/MathCaptain/weakness/src/main/java/MathCaptain/weakness/domain/User/entity/Users.java @@ -5,8 +5,8 @@ import MathCaptain.weakness.domain.Recruitment.entity.Recruitment; import MathCaptain.weakness.domain.User.dto.request.SaveUserRequest; import MathCaptain.weakness.domain.User.dto.request.UpdateUserRequest; -import MathCaptain.weakness.domain.User.enums.TierThresholds; -import MathCaptain.weakness.domain.User.enums.Tiers; +import MathCaptain.weakness.domain.common.TierThresholds; +import MathCaptain.weakness.domain.common.enums.Tiers; import jakarta.persistence.*; import lombok.*; import org.hibernate.validator.constraints.Range; diff --git a/MathCaptain/weakness/src/main/java/MathCaptain/weakness/domain/User/enums/TierThresholds.java b/MathCaptain/weakness/src/main/java/MathCaptain/weakness/domain/common/TierThresholds.java similarity index 90% rename from MathCaptain/weakness/src/main/java/MathCaptain/weakness/domain/User/enums/TierThresholds.java rename to MathCaptain/weakness/src/main/java/MathCaptain/weakness/domain/common/TierThresholds.java index 9c4f49e..e2f30c1 100644 --- a/MathCaptain/weakness/src/main/java/MathCaptain/weakness/domain/User/enums/TierThresholds.java +++ b/MathCaptain/weakness/src/main/java/MathCaptain/weakness/domain/common/TierThresholds.java @@ -1,4 +1,4 @@ -package MathCaptain.weakness.domain.User.enums; +package MathCaptain.weakness.domain.common; public class TierThresholds { public static final long BRONZE = 0; // 기본값 diff --git a/MathCaptain/weakness/src/main/java/MathCaptain/weakness/domain/Group/enums/CategoryStatus.java b/MathCaptain/weakness/src/main/java/MathCaptain/weakness/domain/common/enums/CategoryStatus.java similarity index 93% rename from MathCaptain/weakness/src/main/java/MathCaptain/weakness/domain/Group/enums/CategoryStatus.java rename to MathCaptain/weakness/src/main/java/MathCaptain/weakness/domain/common/enums/CategoryStatus.java index bc3e9d6..eecb388 100644 --- a/MathCaptain/weakness/src/main/java/MathCaptain/weakness/domain/Group/enums/CategoryStatus.java +++ b/MathCaptain/weakness/src/main/java/MathCaptain/weakness/domain/common/enums/CategoryStatus.java @@ -1,4 +1,4 @@ -package MathCaptain.weakness.domain.Group.enums; +package MathCaptain.weakness.domain.common.enums; import com.fasterxml.jackson.annotation.JsonCreator; import com.fasterxml.jackson.annotation.JsonValue; diff --git a/MathCaptain/weakness/src/main/java/MathCaptain/weakness/domain/Chat/enums/ChatRole.java b/MathCaptain/weakness/src/main/java/MathCaptain/weakness/domain/common/enums/ChatRole.java similarity index 50% rename from MathCaptain/weakness/src/main/java/MathCaptain/weakness/domain/Chat/enums/ChatRole.java rename to MathCaptain/weakness/src/main/java/MathCaptain/weakness/domain/common/enums/ChatRole.java index ae9ea9b..e1f9cac 100644 --- a/MathCaptain/weakness/src/main/java/MathCaptain/weakness/domain/Chat/enums/ChatRole.java +++ b/MathCaptain/weakness/src/main/java/MathCaptain/weakness/domain/common/enums/ChatRole.java @@ -1,4 +1,4 @@ -package MathCaptain.weakness.domain.Chat.enums; +package MathCaptain.weakness.domain.common.enums; public enum ChatRole { USER, diff --git a/MathCaptain/weakness/src/main/java/MathCaptain/weakness/domain/Group/enums/GroupRole.java b/MathCaptain/weakness/src/main/java/MathCaptain/weakness/domain/common/enums/GroupRole.java similarity index 93% rename from MathCaptain/weakness/src/main/java/MathCaptain/weakness/domain/Group/enums/GroupRole.java rename to MathCaptain/weakness/src/main/java/MathCaptain/weakness/domain/common/enums/GroupRole.java index 35519b7..e109ba1 100644 --- a/MathCaptain/weakness/src/main/java/MathCaptain/weakness/domain/Group/enums/GroupRole.java +++ b/MathCaptain/weakness/src/main/java/MathCaptain/weakness/domain/common/enums/GroupRole.java @@ -1,4 +1,4 @@ -package MathCaptain.weakness.domain.Group.enums; +package MathCaptain.weakness.domain.common.enums; import com.fasterxml.jackson.annotation.JsonCreator; import com.fasterxml.jackson.annotation.JsonValue; diff --git a/MathCaptain/weakness/src/main/java/MathCaptain/weakness/domain/Recruitment/enums/RecruitmentStatus.java b/MathCaptain/weakness/src/main/java/MathCaptain/weakness/domain/common/enums/RecruitmentStatus.java similarity index 93% rename from MathCaptain/weakness/src/main/java/MathCaptain/weakness/domain/Recruitment/enums/RecruitmentStatus.java rename to MathCaptain/weakness/src/main/java/MathCaptain/weakness/domain/common/enums/RecruitmentStatus.java index be98488..50b5362 100644 --- a/MathCaptain/weakness/src/main/java/MathCaptain/weakness/domain/Recruitment/enums/RecruitmentStatus.java +++ b/MathCaptain/weakness/src/main/java/MathCaptain/weakness/domain/common/enums/RecruitmentStatus.java @@ -1,4 +1,4 @@ -package MathCaptain.weakness.domain.Recruitment.enums; +package MathCaptain.weakness.domain.common.enums; import com.fasterxml.jackson.annotation.JsonCreator; import com.fasterxml.jackson.annotation.JsonValue; diff --git a/MathCaptain/weakness/src/main/java/MathCaptain/weakness/domain/Group/enums/RequestStatus.java b/MathCaptain/weakness/src/main/java/MathCaptain/weakness/domain/common/enums/RequestStatus.java similarity index 94% rename from MathCaptain/weakness/src/main/java/MathCaptain/weakness/domain/Group/enums/RequestStatus.java rename to MathCaptain/weakness/src/main/java/MathCaptain/weakness/domain/common/enums/RequestStatus.java index 34ee4b2..c85b093 100644 --- a/MathCaptain/weakness/src/main/java/MathCaptain/weakness/domain/Group/enums/RequestStatus.java +++ b/MathCaptain/weakness/src/main/java/MathCaptain/weakness/domain/common/enums/RequestStatus.java @@ -1,4 +1,4 @@ -package MathCaptain.weakness.domain.Group.enums; +package MathCaptain.weakness.domain.common.enums; import com.fasterxml.jackson.annotation.JsonCreator; import com.fasterxml.jackson.annotation.JsonValue; diff --git a/MathCaptain/weakness/src/main/java/MathCaptain/weakness/domain/User/enums/Tiers.java b/MathCaptain/weakness/src/main/java/MathCaptain/weakness/domain/common/enums/Tiers.java similarity index 94% rename from MathCaptain/weakness/src/main/java/MathCaptain/weakness/domain/User/enums/Tiers.java rename to MathCaptain/weakness/src/main/java/MathCaptain/weakness/domain/common/enums/Tiers.java index da118c0..68fdefa 100644 --- a/MathCaptain/weakness/src/main/java/MathCaptain/weakness/domain/User/enums/Tiers.java +++ b/MathCaptain/weakness/src/main/java/MathCaptain/weakness/domain/common/enums/Tiers.java @@ -1,4 +1,4 @@ -package MathCaptain.weakness.domain.User.enums; +package MathCaptain.weakness.domain.common.enums; import com.fasterxml.jackson.annotation.JsonCreator; import com.fasterxml.jackson.annotation.JsonValue; diff --git a/MathCaptain/weakness/src/main/java/MathCaptain/weakness/global/auth/filter/GroupRoleFilter.java b/MathCaptain/weakness/src/main/java/MathCaptain/weakness/global/auth/filter/GroupRoleFilter.java index b26db30..3ea9a32 100644 --- a/MathCaptain/weakness/src/main/java/MathCaptain/weakness/global/auth/filter/GroupRoleFilter.java +++ b/MathCaptain/weakness/src/main/java/MathCaptain/weakness/global/auth/filter/GroupRoleFilter.java @@ -1,7 +1,7 @@ package MathCaptain.weakness.global.auth.filter; import MathCaptain.weakness.domain.Group.entity.RelationBetweenUserAndGroup; -import MathCaptain.weakness.domain.Group.enums.GroupRole; +import MathCaptain.weakness.domain.common.enums.GroupRole; import MathCaptain.weakness.domain.Group.repository.RelationRepository; import MathCaptain.weakness.domain.Recruitment.entity.Comment; import MathCaptain.weakness.domain.Recruitment.repository.CommentRepository; diff --git a/MathCaptain/weakness/src/main/java/MathCaptain/weakness/global/auth/jwt/JwtService.java b/MathCaptain/weakness/src/main/java/MathCaptain/weakness/global/auth/jwt/JwtService.java index b93db47..7445573 100644 --- a/MathCaptain/weakness/src/main/java/MathCaptain/weakness/global/auth/jwt/JwtService.java +++ b/MathCaptain/weakness/src/main/java/MathCaptain/weakness/global/auth/jwt/JwtService.java @@ -2,6 +2,7 @@ import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletResponse; +import org.springframework.security.core.Authentication; import java.io.IOException; import java.util.List; @@ -33,4 +34,5 @@ public interface JwtService { boolean isTokenValid(String token); + Authentication getAuthentication(String token); } diff --git a/MathCaptain/weakness/src/main/java/MathCaptain/weakness/global/auth/jwt/JwtServiceImpl.java b/MathCaptain/weakness/src/main/java/MathCaptain/weakness/global/auth/jwt/JwtServiceImpl.java index a07d854..ede92df 100644 --- a/MathCaptain/weakness/src/main/java/MathCaptain/weakness/global/auth/jwt/JwtServiceImpl.java +++ b/MathCaptain/weakness/src/main/java/MathCaptain/weakness/global/auth/jwt/JwtServiceImpl.java @@ -12,6 +12,9 @@ import lombok.Setter; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Value; +import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; +import org.springframework.security.core.Authentication; +import org.springframework.security.core.authority.SimpleGrantedAuthority; import org.springframework.stereotype.Service; import java.io.IOException; @@ -216,5 +219,17 @@ public boolean isTokenValid(String token) { } } + @Override + public Authentication getAuthentication(String accessToken) { + log.info("Authentication 추출"); + return extractEmail(accessToken) + .map(email -> new UsernamePasswordAuthenticationToken( + email, + null, + List.of(new SimpleGrantedAuthority("ROLE_USER")) + )) + .orElseThrow(() -> new RuntimeException("유효하지 않은 토큰입니다.")); + } + } diff --git a/MathCaptain/weakness/src/main/java/MathCaptain/weakness/global/config/AppConfig.java b/MathCaptain/weakness/src/main/java/MathCaptain/weakness/global/config/AppConfig.java new file mode 100644 index 0000000..9c52800 --- /dev/null +++ b/MathCaptain/weakness/src/main/java/MathCaptain/weakness/global/config/AppConfig.java @@ -0,0 +1,23 @@ +package MathCaptain.weakness.global.config; + +import org.springframework.boot.web.client.RestTemplateBuilder; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.web.client.RestTemplate; + +import java.time.Duration; + +@Configuration +public class AppConfig { + /** + * RestTemplate을 빈으로 등록합니다. + * 다른 컴포넌트에서 @Autowired 또는 생성자 주입으로 사용 가능합니다. + */ + @Bean + public RestTemplate restTemplate(RestTemplateBuilder builder) { + return builder + .setConnectTimeout(Duration.ofSeconds(10)) + .setReadTimeout(Duration.ofSeconds(30)) + .build(); + } +} diff --git a/MathCaptain/weakness/src/main/java/MathCaptain/weakness/global/config/WebSocketConfig.java b/MathCaptain/weakness/src/main/java/MathCaptain/weakness/global/config/WebSocketConfig.java index 2442bfe..b3322ec 100644 --- a/MathCaptain/weakness/src/main/java/MathCaptain/weakness/global/config/WebSocketConfig.java +++ b/MathCaptain/weakness/src/main/java/MathCaptain/weakness/global/config/WebSocketConfig.java @@ -12,6 +12,8 @@ @EnableWebSocketMessageBroker public class WebSocketConfig implements WebSocketMessageBrokerConfigurer { + private final WebSocketInterceptor webSocketInterceptor; + @Override public void configureMessageBroker(MessageBrokerRegistry registry) { registry.setApplicationDestinationPrefixes("/send"); //클라이언트에서 보낸 메세지를 받을 prefix @@ -20,7 +22,9 @@ public void configureMessageBroker(MessageBrokerRegistry registry) { @Override public void registerStompEndpoints(StompEndpointRegistry registry) { - registry.addEndpoint("/ws-stomp").setAllowedOriginPatterns("*").withSockJS(); + registry.addEndpoint("/ws-stomp") + .setAllowedOriginPatterns("*") + .addInterceptors(webSocketInterceptor) // ⬅️ interceptor 등록 + .withSockJS(); } - } diff --git a/MathCaptain/weakness/src/main/java/MathCaptain/weakness/global/config/WebSocketInterceptor.java b/MathCaptain/weakness/src/main/java/MathCaptain/weakness/global/config/WebSocketInterceptor.java new file mode 100644 index 0000000..d14d9f4 --- /dev/null +++ b/MathCaptain/weakness/src/main/java/MathCaptain/weakness/global/config/WebSocketInterceptor.java @@ -0,0 +1,57 @@ +package MathCaptain.weakness.global.config; + +import lombok.RequiredArgsConstructor; +import MathCaptain.weakness.global.auth.jwt.JwtService; +import MathCaptain.weakness.domain.User.repository.UserRepository; +import lombok.extern.slf4j.Slf4j; +import org.springframework.http.server.ServerHttpRequest; +import org.springframework.http.server.ServerHttpResponse; +import org.springframework.http.server.ServletServerHttpRequest; +import org.springframework.stereotype.Component; +import org.springframework.web.socket.WebSocketHandler; +import org.springframework.web.socket.server.HandshakeInterceptor; +import org.springframework.security.core.context.SecurityContextHolder; + +import java.util.Map; + +@Slf4j +@Component +@RequiredArgsConstructor +public class WebSocketInterceptor implements HandshakeInterceptor { + + private final JwtService jwtService; + private final UserRepository userRepository; + + @Override + public boolean beforeHandshake( + ServerHttpRequest request, + ServerHttpResponse response, + WebSocketHandler wsHandler, + Map attributes + ) throws Exception { + if (request instanceof ServletServerHttpRequest servletRequest) { + String authHeader = servletRequest.getServletRequest().getHeader("Authorization"); + + if (authHeader != null && authHeader.startsWith("Bearer ")) { + String token = authHeader.substring(7); + + log.info("===== 토큰 유효성 검사(ws) ====="); + if (jwtService.isTokenValid(token)) { + log.info("유효성 검사 통과 ✅"); + var authentication = jwtService.getAuthentication(token); + SecurityContextHolder.getContext().setAuthentication(authentication); + attributes.put("user", authentication); // 나중에 메시지에서 접근 가능 + } + } + } + return true; + } + + @Override + public void afterHandshake(ServerHttpRequest request, + ServerHttpResponse response, + WebSocketHandler wsHandler, + Exception exception) { + // 없음 + } +} \ No newline at end of file