From 1d333942643a11f98b5e100fd412426222f80eaa Mon Sep 17 00:00:00 2001 From: kkw610 Date: Sun, 24 May 2026 11:39:06 +0900 Subject: [PATCH 1/3] =?UTF-8?q?fix:=20createQuestion=20userID=20=ED=83=80?= =?UTF-8?q?=EC=9E=85=20Integer=E2=86=92Long=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../domain/question/controller/QuestionController.java | 4 ++-- 1 file changed, 2 insertions(+), 2 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 3561d8d..86a5814 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 @@ -44,10 +44,10 @@ public ResponseEntity> getQue public ResponseEntity> createQuestion( @PathVariable Long sessionId, @RequestBody QuestionReqDTO.CreateReq request, - @AuthenticationPrincipal Integer userId + @AuthenticationPrincipal Long userId ) { return ResponseUtil.success(QuestionSuccessCode.QUESTION_CREATED, - questionService.createQuestion(sessionId, request, Long.valueOf(userId))); + questionService.createQuestion(sessionId, request, userId)); } // 댓글/대댓글 등록 From 09ced4143bc90a999d979a7ff0c4d9a15ba783ba Mon Sep 17 00:00:00 2001 From: kkw610 Date: Sun, 24 May 2026 11:46:22 +0900 Subject: [PATCH 2/3] =?UTF-8?q?feat:=20=EC=A7=88=EB=AC=B8=20=EC=88=98?= =?UTF-8?q?=EC=A0=95=20API=20=EA=B5=AC=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 | 10 ++++++++ .../domain/question/entity/Question.java | 6 +++++ .../exception/code/QuestionSuccessCode.java | 3 ++- .../question/service/QuestionService.java | 25 +++++++++++++++++++ 6 files changed, 62 insertions(+), 1 deletion(-) 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 86a5814..10a18e8 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)); } + // 질문 수정 + // PATCH /api/questions/{questionId}/modify + @PatchMapping("/api/questions/{questionId}/modify") + public ResponseEntity> updateQuestion( + @PathVariable Long questionId, + @RequestBody QuestionReqDTO.UpdateReq request, + @AuthenticationPrincipal Long userId + ) { + return ResponseUtil.success(QuestionSuccessCode.QUESTION_UPDATED, + questionService.updateQuestion(questionId, request, userId)); + } + // 이해도 체크 생성 // POST /api/sessions/{sessionId}/understanding-checks @PostMapping("/api/sessions/{sessionId}/understanding-checks") 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 ca6b46b..a902d6b 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 @@ -13,6 +13,13 @@ public static class CreateReq { private String content; } + // 질문 수정 요청 + @Getter + @NoArgsConstructor + public static class UpdateReq { + private String content; + } + // 댓글/대댓글 등록 요청 // parentCommentId가 null이면 일반 댓글, 값이 있으면 대댓글 @Getter 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 c2804eb..3dcdc0a 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 @@ -50,6 +50,16 @@ public record LikeRes( ) { } + // 질문 수정/삭제 응답 (형태가 동일해서 하나로 공유) + // deletedAt에 값이 있으면 삭제된 상태 + public record UpdateDeleteRes( + Long id, + String content, + LocalDateTime updatedAt, + LocalDateTime deletedAt + ) { + } + // 질문 상세 응답 public record QuestionDetailResponse( Long questionId, diff --git a/backend/src/main/java/com/example/Piroin/project/domain/question/entity/Question.java b/backend/src/main/java/com/example/Piroin/project/domain/question/entity/Question.java index a099370..9be04f8 100644 --- a/backend/src/main/java/com/example/Piroin/project/domain/question/entity/Question.java +++ b/backend/src/main/java/com/example/Piroin/project/domain/question/entity/Question.java @@ -67,4 +67,10 @@ public void decreaseLikeCount() { } this.updatedAt = LocalDateTime.now(); } + + // 질문 내용 수정 + public void updateContent(String content) { + this.content = content; + this.updatedAt = LocalDateTime.now(); + } } \ No newline at end of file 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 94d5f4c..6828e50 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 @@ -11,8 +11,9 @@ public enum QuestionSuccessCode implements BaseCode { QUESTION_ROOM_OK(HttpStatus.OK, "QUESTION200_1", "질문 방 조회에 성공했습니다."), QUESTION_DETAIL_OK(HttpStatus.OK, "QUESTION200_2", "질문 상세 조회에 성공했습니다."), - LIKE_TOGGLED(HttpStatus.OK, "QUESTION200_3", "좋아요가 처리되었습니다."), // ← 추가 + LIKE_TOGGLED(HttpStatus.OK, "QUESTION200_3", "좋아요가 처리되었습니다."), UNDERSTANDING_RESPONSE_OK(HttpStatus.OK, "QUESTION200_4", "이해도 응답이 반영되었습니다."), + QUESTION_UPDATED(HttpStatus.OK, "QUESTION200_5", "질문이 수정되었습니다."), QUESTION_CREATED(HttpStatus.CREATED, "QUESTION201_1", "질문이 등록되었습니다."), COMMENT_CREATED(HttpStatus.CREATED, "QUESTION201_2", "댓글이 등록되었습니다."), UNDERSTANDING_CHECK_CREATED(HttpStatus.CREATED, "QUESTION201_3", "이해도 체크가 생성되었습니다."); 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 44d3324..de48200 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,25 @@ public QuestionResDTO.LikeRes toggleLike(Long questionId, Long userId) { }); } + // 질문 수정 + @Transactional + public QuestionResDTO.UpdateDeleteRes updateQuestion( + Long questionId, + QuestionReqDTO.UpdateReq request, + Long userId + ) { + User loginUser = findLoginUser(userId); + Question question = findQuestion(questionId); + validateQuestionOwner(question, loginUser); + + question.updateContent(request.getContent()); + + return new QuestionResDTO.UpdateDeleteRes( + question.getId(), question.getContent(), + question.getUpdatedAt(), question.getDeletedAt() + ); + } + // 이해도 체크 생성 @Transactional public QuestionResDTO.UnderstandingCheckCreateResponse createUnderstandingCheck( @@ -320,6 +339,12 @@ private void validateCheckBelongsToSession(UnderstandingCheck check, StudySessio } } + private void validateQuestionOwner(Question question, User loginUser) { + if (!question.getUser().getId().equals(loginUser.getId())) { + throw new QuestionException(HttpStatus.FORBIDDEN, "본인의 질문만 수정/삭제할 수 있습니다."); + } + } + private UnderstandResChoice applyUnderstandingResponse( UnderstandingCheck check, User loginUser, UnderstandResChoice requestedChoice ) { From caf818aefad978a4945c4515c2e9b8ffe082610a Mon Sep 17 00:00:00 2001 From: kkw610 Date: Sun, 24 May 2026 11:49:06 +0900 Subject: [PATCH 3/3] =?UTF-8?q?feat:=20=EC=A7=88=EB=AC=B8=20=EC=82=AD?= =?UTF-8?q?=EC=A0=9C=20API=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../question/controller/QuestionController.java | 11 +++++++++++ .../project/domain/question/entity/Question.java | 6 ++++++ .../exception/code/QuestionSuccessCode.java | 1 + .../domain/question/service/QuestionService.java | 15 +++++++++++++++ 4 files changed, 33 insertions(+) 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 10a18e8..e783b15 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 @@ -85,6 +85,17 @@ public ResponseEntity> updateQuestio questionService.updateQuestion(questionId, request, userId)); } + // 질문 삭제 + // DELETE /api/questions/{questionId} + @DeleteMapping("/api/questions/{questionId}") + public ResponseEntity> deleteQuestion( + @PathVariable Long questionId, + @AuthenticationPrincipal Long userId + ) { + return ResponseUtil.success(QuestionSuccessCode.QUESTION_DELETED, + questionService.deleteQuestion(questionId, userId)); + } + // 이해도 체크 생성 // POST /api/sessions/{sessionId}/understanding-checks @PostMapping("/api/sessions/{sessionId}/understanding-checks") diff --git a/backend/src/main/java/com/example/Piroin/project/domain/question/entity/Question.java b/backend/src/main/java/com/example/Piroin/project/domain/question/entity/Question.java index 9be04f8..6168ec5 100644 --- a/backend/src/main/java/com/example/Piroin/project/domain/question/entity/Question.java +++ b/backend/src/main/java/com/example/Piroin/project/domain/question/entity/Question.java @@ -73,4 +73,10 @@ public void updateContent(String content) { this.content = content; this.updatedAt = LocalDateTime.now(); } + + // 질문 소프트 삭제 (DB에서 실제로 지우지 않고 deleted_at에 시각 기록) + public void softDelete() { + this.deletedAt = LocalDateTime.now(); + this.updatedAt = LocalDateTime.now(); + } } \ No newline at end of file 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 6828e50..2a1bd5d 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,6 +14,7 @@ public enum QuestionSuccessCode implements BaseCode { LIKE_TOGGLED(HttpStatus.OK, "QUESTION200_3", "좋아요가 처리되었습니다."), UNDERSTANDING_RESPONSE_OK(HttpStatus.OK, "QUESTION200_4", "이해도 응답이 반영되었습니다."), QUESTION_UPDATED(HttpStatus.OK, "QUESTION200_5", "질문이 수정되었습니다."), + QUESTION_DELETED(HttpStatus.OK, "QUESTION200_6", "질문이 삭제되었습니다."), QUESTION_CREATED(HttpStatus.CREATED, "QUESTION201_1", "질문이 등록되었습니다."), COMMENT_CREATED(HttpStatus.CREATED, "QUESTION201_2", "댓글이 등록되었습니다."), UNDERSTANDING_CHECK_CREATED(HttpStatus.CREATED, "QUESTION201_3", "이해도 체크가 생성되었습니다."); 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 de48200..a4c5f65 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 @@ -256,6 +256,21 @@ public QuestionResDTO.UpdateDeleteRes updateQuestion( ); } + // 질문 삭제 + @Transactional + public QuestionResDTO.UpdateDeleteRes deleteQuestion(Long questionId, Long userId) { + User loginUser = findLoginUser(userId); + Question question = findQuestion(questionId); + validateQuestionOwner(question, loginUser); + + question.softDelete(); + + return new QuestionResDTO.UpdateDeleteRes( + question.getId(), question.getContent(), + question.getUpdatedAt(), question.getDeletedAt() + ); + } + // 이해도 체크 생성 @Transactional public QuestionResDTO.UnderstandingCheckCreateResponse createUnderstandingCheck(