diff --git a/backend/src/main/java/org/example/backend/domain/question/controller/QuestionController.java b/backend/src/main/java/org/example/backend/domain/question/controller/QuestionController.java index df392dca..4296c78c 100644 --- a/backend/src/main/java/org/example/backend/domain/question/controller/QuestionController.java +++ b/backend/src/main/java/org/example/backend/domain/question/controller/QuestionController.java @@ -9,10 +9,7 @@ import org.example.backend.global.security.auth.CustomUserDetails; import org.springframework.security.core.Authentication; import org.springframework.security.core.context.SecurityContextHolder; -import org.springframework.web.bind.annotation.GetMapping; -import org.springframework.web.bind.annotation.PathVariable; -import org.springframework.web.bind.annotation.RequestMapping; -import org.springframework.web.bind.annotation.RestController; +import org.springframework.web.bind.annotation.*; import java.util.List; import java.util.UUID; @@ -45,4 +42,11 @@ public ApiResponse> getBeforeChatting(@ List result = questionService.getBeforeChatting(lectureId); return ApiResponse.onSuccess(result); } + + // 채팅 내용 저장하기 + @PostMapping("/chatting/after/{lectureId}") + public ApiResponse afterChatting(@PathVariable("lectureId") UUID lectureId) { + questionService.saveChatting(lectureId); + return ApiResponse.onSuccess(null); + } } diff --git a/backend/src/main/java/org/example/backend/domain/question/converter/QuestionConverter.java b/backend/src/main/java/org/example/backend/domain/question/converter/QuestionConverter.java index 7d1491d5..73bfc30e 100644 --- a/backend/src/main/java/org/example/backend/domain/question/converter/QuestionConverter.java +++ b/backend/src/main/java/org/example/backend/domain/question/converter/QuestionConverter.java @@ -1,9 +1,12 @@ package org.example.backend.domain.question.converter; import lombok.RequiredArgsConstructor; +import org.example.backend.domain.lecture.entity.Lecture; import org.example.backend.domain.question.dto.response.QuestionResponseDTO; import org.example.backend.domain.question.entity.Question; +import org.example.backend.domain.user.entity.Role; import org.example.backend.domain.user.entity.SocialType; +import org.example.backend.domain.user.entity.User; import org.example.backend.global.S3.service.S3Service; import org.springframework.stereotype.Component; @@ -32,4 +35,13 @@ public QuestionResponseDTO.student toStudentQuestions(Question question) { .timestamp(question.getTimestamp()) .build(); } + + public Question toQuestion(QuestionResponseDTO.afterChatting dto, Lecture lecture, User user) { + return Question.builder() + .lecture(lecture) + .timestamp(dto.getTimestamp()) + .content(dto.getContent()) + .user(user) + .build(); + } } diff --git a/backend/src/main/java/org/example/backend/domain/question/dto/response/QuestionResponseDTO.java b/backend/src/main/java/org/example/backend/domain/question/dto/response/QuestionResponseDTO.java index 6ea620f0..5268c678 100644 --- a/backend/src/main/java/org/example/backend/domain/question/dto/response/QuestionResponseDTO.java +++ b/backend/src/main/java/org/example/backend/domain/question/dto/response/QuestionResponseDTO.java @@ -44,4 +44,15 @@ public static class beforeChatting { private Role role; } + @Builder + @Getter + @NoArgsConstructor + @AllArgsConstructor + public static class afterChatting { + private String content; + private LocalDateTime timestamp; + private UUID senderId; + private Role role; + } + } diff --git a/backend/src/main/java/org/example/backend/domain/question/exception/QuestionErrorCode.java b/backend/src/main/java/org/example/backend/domain/question/exception/QuestionErrorCode.java index a82fcee7..f54fa5dc 100644 --- a/backend/src/main/java/org/example/backend/domain/question/exception/QuestionErrorCode.java +++ b/backend/src/main/java/org/example/backend/domain/question/exception/QuestionErrorCode.java @@ -11,7 +11,8 @@ public enum QuestionErrorCode implements BaseErrorCode { _FORBIDDEN_LECTURE_ACCESS(HttpStatus.FORBIDDEN,"QUESTION403_1","해당 강의를 수강중인 학생만 조회가능합니다."), _CHAT_MESSAGE_SEND_FAIL(HttpStatus.INTERNAL_SERVER_ERROR, "QUESTION500_1", "채팅 메시지 전송에 실패했습니다."), - _INVALID_JSON_FORMAT(HttpStatus.BAD_REQUEST,"QUESTION400_1","저장된 채팅 데이터를 읽는 데 실패했습니다."); + _INVALID_JSON_FORMAT(HttpStatus.BAD_REQUEST,"QUESTION400_1","저장된 채팅 데이터를 읽는 데 실패했습니다."), + _FORBIDDEN_CHATTING_ACCESS(HttpStatus.FORBIDDEN,"QUESTION403_2","채팅 저장 권한은 강사에게만 있습니다."); private final HttpStatus httpStatus; private final String code; diff --git a/backend/src/main/java/org/example/backend/domain/question/service/QuestionService.java b/backend/src/main/java/org/example/backend/domain/question/service/QuestionService.java index 0af69859..092ee85d 100644 --- a/backend/src/main/java/org/example/backend/domain/question/service/QuestionService.java +++ b/backend/src/main/java/org/example/backend/domain/question/service/QuestionService.java @@ -13,4 +13,6 @@ public interface QuestionService { List getStudentQuestions(UUID lectureId, User user); // 이전 채팅 내용 불러오개 List getBeforeChatting(UUID lectureId); + // 채팅 내용 저장 + void saveChatting(UUID lectureId); } diff --git a/backend/src/main/java/org/example/backend/domain/question/service/QuestionServiceImpl.java b/backend/src/main/java/org/example/backend/domain/question/service/QuestionServiceImpl.java index 02717cfb..160b2ea9 100644 --- a/backend/src/main/java/org/example/backend/domain/question/service/QuestionServiceImpl.java +++ b/backend/src/main/java/org/example/backend/domain/question/service/QuestionServiceImpl.java @@ -3,13 +3,23 @@ import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.ObjectMapper; import lombok.RequiredArgsConstructor; +import org.example.backend.domain.lecture.entity.Lecture; +import org.example.backend.domain.lecture.exception.LectureErrorCode; +import org.example.backend.domain.lecture.exception.LectureException; +import org.example.backend.domain.lecture.repository.LectureRepository; import org.example.backend.domain.question.converter.QuestionConverter; import org.example.backend.domain.question.dto.response.QuestionResponseDTO; +import org.example.backend.domain.question.entity.Question; import org.example.backend.domain.question.exception.QuestionErrorCode; import org.example.backend.domain.question.exception.QuestionException; import org.example.backend.domain.question.repository.QuestionRepository; import org.example.backend.domain.studentClass.repository.StudentClassRepository; +import org.example.backend.domain.user.entity.Role; import org.example.backend.domain.user.entity.User; +import org.example.backend.domain.user.exception.UserErrorCode; +import org.example.backend.domain.user.exception.UserException; +import org.example.backend.domain.user.repository.UserRepository; +import org.example.backend.global.security.auth.CustomSecurityUtil; import org.springframework.data.redis.core.RedisTemplate; import org.springframework.stereotype.Service; @@ -24,9 +34,12 @@ public class QuestionServiceImpl implements QuestionService { private final QuestionRepository questionRepository; private final StudentClassRepository studentClassRepository; + private final LectureRepository lectureRepository; + private final UserRepository userRepository; private final QuestionConverter questionConverter; private final RedisTemplate redisTemplate; private final ObjectMapper objectMapper; + private final CustomSecurityUtil customSecurityUtil; @Override public List getTeacherQuestions(UUID lectureId, User user ) { @@ -78,4 +91,59 @@ public List getBeforeChatting(UUID lectureId } return chatList; } + + @Override + public void saveChatting(UUID lectureId) { + // 1. 접근 권한 확인 + Role role = customSecurityUtil.getUserRole(); + if(role != Role.TEACHER){ + throw new QuestionException(QuestionErrorCode._FORBIDDEN_CHATTING_ACCESS); + } + + // 2. 강의 존재 확인 + Lecture lecture = lectureRepository.findById(lectureId) + .orElseThrow(() -> new LectureException(LectureErrorCode.LECTURE_NOT_FOUND)); + + // 3. redis key 정의 + String redisKey = "chat:lecture:" + lectureId; + + // 4. redis에서 채팅 내역 가져오기 + List rawMessages = redisTemplate.opsForList().range(redisKey, 0, -1); + + if(rawMessages == null || rawMessages.isEmpty()){ + // 이전 내용이 없다면 저장 X + return; + } + + // 5. JSON -> Entity 변환 + List questionEntities = new ArrayList<>(); + for(String raw: rawMessages){ + try{ + // JSON -> DTO + QuestionResponseDTO.afterChatting dto = objectMapper.readValue(raw, QuestionResponseDTO.afterChatting.class); + + // 강사 메시지 저장 X + if(dto.getRole() == Role.TEACHER){ + continue; + } + // DTO -> Entity + User user = userRepository.findById(dto.getSenderId()) + .orElseThrow(() -> new UserException(UserErrorCode._USER_NOT_FOUND)); + + Question question = questionConverter.toQuestion(dto,lecture,user); + + // Entity -> list에 넣기 + questionEntities.add(question); + } + catch (JsonProcessingException e){ + throw new QuestionException(QuestionErrorCode._INVALID_JSON_FORMAT); + } + } + + // 6. DB 저장 + questionRepository.saveAll(questionEntities); + + // 7. Redis 비우기 + redisTemplate.delete(redisKey); + } }