Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -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<ChatResponse> llmResponses = chatService.askAI(loginUser, request);
llmResponses.forEach(aiResp ->
template.convertAndSend("/sub/" + loginUser.getUserId(), aiResp)
);
}
}
Original file line number Diff line number Diff line change
@@ -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);
}
}
Original file line number Diff line number Diff line change
@@ -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<Chat> history;

private LLMRequest(Long userId, String message, List<Chat> history) {
this.userId = userId;
this.message = message;
this.history = history;
}

public static LLMRequest of(Users loginUser, ChatRequest request, List<Chat> history) {
return new LLMRequest(loginUser.getUserId(), request.getMessage(), history);
}
}
Original file line number Diff line number Diff line change
@@ -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()
);
}
}
Original file line number Diff line number Diff line change
@@ -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;
Expand Down Expand Up @@ -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();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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<Chat, Long> {
List<Chat> findAllByUserIdOrderBySendTimeAsc(Long userId);
}
Original file line number Diff line number Diff line change
@@ -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<ChatResponse> askAI(Users loginUser, ChatRequest request) {
List<Chat> history = chatRepository.findAllByUserIdOrderBySendTimeAsc(loginUser.getUserId());
List<Chat> 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);
}
}
Original file line number Diff line number Diff line change
@@ -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<Chat> call(Users loginUser, List<Chat> history, ChatRequest request) {
try {
LLMRequest llmRequest = LLMRequest.of(loginUser, request, history);

ResponseEntity<ChatResponse[]> 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();
}
}
}
Original file line number Diff line number Diff line change
@@ -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;
Expand Down
Original file line number Diff line number Diff line change
@@ -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.*;
Expand Down
Original file line number Diff line number Diff line change
@@ -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.*;

Expand Down
Original file line number Diff line number Diff line change
@@ -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.*;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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.*;

Expand Down
Original file line number Diff line number Diff line change
@@ -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.*;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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.*;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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.*;
Expand Down
Original file line number Diff line number Diff line change
@@ -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;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
Loading