From ef2eb1de6b4f27c47ec33370b16ba2ac06dbe938 Mon Sep 17 00:00:00 2001 From: issuejong Date: Mon, 18 May 2026 13:29:47 +0900 Subject: [PATCH 1/2] =?UTF-8?q?[Feat]=20=EC=9D=B4=ED=95=B4=EB=8F=84=20?= =?UTF-8?q?=EC=B2=B4=ED=81=AC=20=EC=83=9D=EC=84=B1=20API=20=EA=B5=AC?= =?UTF-8?q?=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../controller/QuestionController.java | 12 ++++++ .../domain/question/dto/QuestionReqDTO.java | 7 ++++ .../domain/question/dto/QuestionResDTO.java | 12 +++++- .../exception/code/QuestionSuccessCode.java | 3 +- .../question/service/QuestionService.java | 38 ++++++++++++++++++- .../project/global/config/SecurityConfig.java | 3 ++ 6 files changed, 71 insertions(+), 4 deletions(-) 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..d7f4d06 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,6 +287,18 @@ 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, "질문을 찾을 수 없습니다.")); @@ -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..036bcf3 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/*/understanding-checks").hasRole("ADMIN") + // Swagger .requestMatchers( "/swagger-ui/**", From 84da19a1f353c9c03be089f90c6a998925b6c361 Mon Sep 17 00:00:00 2001 From: issuejong Date: Mon, 18 May 2026 13:46:04 +0900 Subject: [PATCH 2/2] =?UTF-8?q?[Fix]=20StudySession=20ID=20=ED=83=80?= =?UTF-8?q?=EC=9E=85=20=EC=A0=95=ED=95=A9=EC=84=B1=20=EB=B0=8F=20=EB=B3=B4?= =?UTF-8?q?=EC=95=88=20=EB=A7=A4=EC=B2=98=20=EC=A0=95=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../domain/curriculum/repository/CurriculumRepository.java | 2 +- .../project/domain/curriculum/service/CurriculumService.java | 4 ++-- .../project/domain/question/service/QuestionService.java | 2 +- .../example/Piroin/project/global/config/SecurityConfig.java | 2 +- 4 files changed, 5 insertions(+), 5 deletions(-) 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/service/QuestionService.java b/backend/src/main/java/com/example/Piroin/project/domain/question/service/QuestionService.java index d7f4d06..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 @@ -305,7 +305,7 @@ private Question findQuestion(Long questionId) { } private StudySession findSession(Long sessionId) { - return curriculumRepository.findById(Math.toIntExact(sessionId)) + return curriculumRepository.findById(sessionId) .orElseThrow(() -> new QuestionException(HttpStatus.NOT_FOUND, "세션을 찾을 수 없습니다.")); } 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 036bcf3..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 @@ -40,7 +40,7 @@ public SecurityFilterChain filterChain(HttpSecurity http) throws Exception { .requestMatchers(HttpMethod.DELETE, "/api/curriculums/{id}").hasRole("ADMIN") // understanding check: 생성은 ADMIN만 가능 - .requestMatchers(HttpMethod.POST, "/api/sessions/*/understanding-checks").hasRole("ADMIN") + .requestMatchers(HttpMethod.POST, "/api/sessions/{sessionId}/understanding-checks").hasRole("ADMIN") // Swagger .requestMatchers(