diff --git a/backend/src/main/java/com/example/Piroin/project/domain/curriculum/repository/CurriculumRepository.java b/backend/src/main/java/com/example/Piroin/project/domain/curriculum/repository/CurriculumRepository.java index aea0b3f..8989cd9 100644 --- a/backend/src/main/java/com/example/Piroin/project/domain/curriculum/repository/CurriculumRepository.java +++ b/backend/src/main/java/com/example/Piroin/project/domain/curriculum/repository/CurriculumRepository.java @@ -11,7 +11,7 @@ Q&A 서비스에서 세션 존재 여부 확인 시 사용 JpaRepository<엔티티 타입, PK 타입> 을 상속하면 findById, save, delete 등 기본 메서드가 자동으로 제공 */ -public interface CurriculumRepository extends JpaRepository { +public interface CurriculumRepository extends JpaRepository { List findByStatusOrderBySessionDateAscDayPartAsc(SessionStatus status); List findByStatusOrderBySessionDateDescDayPartDesc(SessionStatus status); diff --git a/backend/src/main/java/com/example/Piroin/project/domain/curriculum/service/CurriculumService.java b/backend/src/main/java/com/example/Piroin/project/domain/curriculum/service/CurriculumService.java index f53e27c..6599d79 100644 --- a/backend/src/main/java/com/example/Piroin/project/domain/curriculum/service/CurriculumService.java +++ b/backend/src/main/java/com/example/Piroin/project/domain/curriculum/service/CurriculumService.java @@ -50,7 +50,7 @@ public CurriculumResDTO.CreateSessionRes createSession(CurriculumReqDTO.CreateSe @Transactional public CurriculumResDTO.UpdateSessionRes updateSession(Long sessionId, CurriculumReqDTO.UpdateSessionReq req) { - StudySession session = curriculumRepository.findById(Math.toIntExact(sessionId)) + StudySession session = curriculumRepository.findById(sessionId) .orElseThrow(() -> new CurriculumException(HttpStatus.NOT_FOUND, "세션을 찾을 수 없습니다.")); session.update(req.getGeneration(), req.getWeek(), req.getSessionDate(), req.getDayPart(), @@ -63,7 +63,7 @@ public CurriculumResDTO.UpdateSessionRes updateSession(Long sessionId, Curriculu @Transactional public void deleteSession(Long sessionId) { - StudySession session = curriculumRepository.findById(Math.toIntExact(sessionId)) + StudySession session = curriculumRepository.findById(sessionId) .orElseThrow(() -> new CurriculumException(HttpStatus.NOT_FOUND, "세션을 찾을 수 없습니다.")); curriculumRepository.delete(session); diff --git a/backend/src/main/java/com/example/Piroin/project/domain/question/controller/QuestionController.java b/backend/src/main/java/com/example/Piroin/project/domain/question/controller/QuestionController.java index 49ae66c..3561d8d 100644 --- a/backend/src/main/java/com/example/Piroin/project/domain/question/controller/QuestionController.java +++ b/backend/src/main/java/com/example/Piroin/project/domain/question/controller/QuestionController.java @@ -73,6 +73,18 @@ public ResponseEntity> toggleLike( questionService.toggleLike(questionId, userId)); } + // 이해도 체크 생성 + // POST /api/sessions/{sessionId}/understanding-checks + @PostMapping("/api/sessions/{sessionId}/understanding-checks") + public ResponseEntity> createUnderstandingCheck( + @PathVariable Long sessionId, + @RequestBody QuestionReqDTO.UnderstandingCheckCreateReq request, + @AuthenticationPrincipal Long userId + ) { + return ResponseUtil.success(QuestionSuccessCode.UNDERSTANDING_CHECK_CREATED, + questionService.createUnderstandingCheck(sessionId, request, userId)); + } + // 이해도 체크 응답 // POST /api/sessions/{sessionId}/understanding-checks/{checkId}/responses @PostMapping("/api/sessions/{sessionId}/understanding-checks/{checkId}/responses") diff --git a/backend/src/main/java/com/example/Piroin/project/domain/question/dto/QuestionReqDTO.java b/backend/src/main/java/com/example/Piroin/project/domain/question/dto/QuestionReqDTO.java index d06caef..ca6b46b 100644 --- a/backend/src/main/java/com/example/Piroin/project/domain/question/dto/QuestionReqDTO.java +++ b/backend/src/main/java/com/example/Piroin/project/domain/question/dto/QuestionReqDTO.java @@ -28,4 +28,11 @@ public static class CommentReq { public static class UnderstandingResponseReq { private UnderstandResChoice choice; } + + // 이해도 체크 생성 요청 + @Getter + @NoArgsConstructor + public static class UnderstandingCheckCreateReq { + private String content; + } } diff --git a/backend/src/main/java/com/example/Piroin/project/domain/question/dto/QuestionResDTO.java b/backend/src/main/java/com/example/Piroin/project/domain/question/dto/QuestionResDTO.java index 665d79b..c2804eb 100644 --- a/backend/src/main/java/com/example/Piroin/project/domain/question/dto/QuestionResDTO.java +++ b/backend/src/main/java/com/example/Piroin/project/domain/question/dto/QuestionResDTO.java @@ -105,8 +105,7 @@ public record UnderstandingSliceResponse( public record UnderstandingCheckResponse( Long checkId, - String title, - String description, + String content, Integer understoodCount, Integer notUnderstoodCount, LocalDateTime createdAt @@ -139,4 +138,13 @@ public record UnderstandingResponseResult( Integer notUnderstoodCount ) { } + + public record UnderstandingCheckCreateResponse( + Long checkId, + String content, + Integer understoodCount, + Integer notUnderstoodCount, + LocalDateTime createdAt + ) { + } } diff --git a/backend/src/main/java/com/example/Piroin/project/domain/question/exception/code/QuestionSuccessCode.java b/backend/src/main/java/com/example/Piroin/project/domain/question/exception/code/QuestionSuccessCode.java index 9a3dba7..94d5f4c 100644 --- a/backend/src/main/java/com/example/Piroin/project/domain/question/exception/code/QuestionSuccessCode.java +++ b/backend/src/main/java/com/example/Piroin/project/domain/question/exception/code/QuestionSuccessCode.java @@ -14,7 +14,8 @@ public enum QuestionSuccessCode implements BaseCode { LIKE_TOGGLED(HttpStatus.OK, "QUESTION200_3", "좋아요가 처리되었습니다."), // ← 추가 UNDERSTANDING_RESPONSE_OK(HttpStatus.OK, "QUESTION200_4", "이해도 응답이 반영되었습니다."), QUESTION_CREATED(HttpStatus.CREATED, "QUESTION201_1", "질문이 등록되었습니다."), - COMMENT_CREATED(HttpStatus.CREATED, "QUESTION201_2", "댓글이 등록되었습니다."); + COMMENT_CREATED(HttpStatus.CREATED, "QUESTION201_2", "댓글이 등록되었습니다."), + UNDERSTANDING_CHECK_CREATED(HttpStatus.CREATED, "QUESTION201_3", "이해도 체크가 생성되었습니다."); private final HttpStatus status; private final String code; diff --git a/backend/src/main/java/com/example/Piroin/project/domain/question/service/QuestionService.java b/backend/src/main/java/com/example/Piroin/project/domain/question/service/QuestionService.java index c79fd40..44d3324 100644 --- a/backend/src/main/java/com/example/Piroin/project/domain/question/service/QuestionService.java +++ b/backend/src/main/java/com/example/Piroin/project/domain/question/service/QuestionService.java @@ -237,6 +237,30 @@ public QuestionResDTO.LikeRes toggleLike(Long questionId, Long userId) { }); } + // 이해도 체크 생성 + @Transactional + public QuestionResDTO.UnderstandingCheckCreateResponse createUnderstandingCheck( + Long sessionId, QuestionReqDTO.UnderstandingCheckCreateReq request, Long userId + ) { + validateUnderstandingCheckCreateRequest(request); + User loginUser = findLoginUser(userId); + validateAdmin(loginUser); + StudySession session = findSession(sessionId); + + LocalDateTime now = LocalDateTime.now(); + UnderstandingCheck check = understandingCheckRepository.save(UnderstandingCheck.builder() + .session(session) + .createdBy(loginUser) + .title(request.getContent().trim()) + .createdAt(now) + .updatedAt(now) + .build()); + + return new QuestionResDTO.UnderstandingCheckCreateResponse( + check.getId(), check.getTitle(), 0, 0, check.getCreatedAt() + ); + } + // 이해도 체크 응답 @Transactional public QuestionResDTO.UnderstandingResponseResult respondUnderstandingCheck( @@ -263,13 +287,25 @@ private User findLoginUser(Long userId) { .orElseThrow(() -> new QuestionException(HttpStatus.UNAUTHORIZED, "로그인 사용자를 찾을 수 없습니다.")); } + private void validateAdmin(User loginUser) { + if (loginUser.getRole() != Role.ADMIN) { + throw new QuestionException(HttpStatus.FORBIDDEN, "관리자만 이해도 체크를 생성할 수 있습니다."); + } + } + + private void validateUnderstandingCheckCreateRequest(QuestionReqDTO.UnderstandingCheckCreateReq request) { + if (request == null || request.getContent() == null || request.getContent().isBlank()) { + throw new IllegalArgumentException("이해도 체크 내용은 필수입니다."); + } + } + private Question findQuestion(Long questionId) { return questionRepository.findByIdAndDeletedAtIsNull(questionId) .orElseThrow(() -> new QuestionException(HttpStatus.NOT_FOUND, "질문을 찾을 수 없습니다.")); } private StudySession findSession(Long sessionId) { - return curriculumRepository.findById(Math.toIntExact(sessionId)) + return curriculumRepository.findById(sessionId) .orElseThrow(() -> new QuestionException(HttpStatus.NOT_FOUND, "세션을 찾을 수 없습니다.")); } @@ -346,7 +382,7 @@ private QuestionResDTO.UnderstandingSliceResponse getUnderstandingSlice(StudySes private QuestionResDTO.UnderstandingCheckResponse toUnderstandingCheckResponse(UnderstandingCheck check) { return new QuestionResDTO.UnderstandingCheckResponse( - check.getId(), check.getTitle(), check.getDescription(), + check.getId(), check.getTitle(), understandingResponseRepository.countByCheckAndChoice(check, UnderstandResChoice.UNDERSTOOD), understandingResponseRepository.countByCheckAndChoice(check, UnderstandResChoice.NOT_UNDERSTOOD), check.getCreatedAt() diff --git a/backend/src/main/java/com/example/Piroin/project/global/config/SecurityConfig.java b/backend/src/main/java/com/example/Piroin/project/global/config/SecurityConfig.java index 3a22243..5387ffc 100644 --- a/backend/src/main/java/com/example/Piroin/project/global/config/SecurityConfig.java +++ b/backend/src/main/java/com/example/Piroin/project/global/config/SecurityConfig.java @@ -39,6 +39,9 @@ public SecurityFilterChain filterChain(HttpSecurity http) throws Exception { .requestMatchers(HttpMethod.PATCH, "/api/curriculums/{id}").hasRole("ADMIN") .requestMatchers(HttpMethod.DELETE, "/api/curriculums/{id}").hasRole("ADMIN") + // understanding check: 생성은 ADMIN만 가능 + .requestMatchers(HttpMethod.POST, "/api/sessions/{sessionId}/understanding-checks").hasRole("ADMIN") + // Swagger .requestMatchers( "/swagger-ui/**",