diff --git a/.github/workflows/gradle.yml b/.github/workflows/gradle.yml
index b031374..49ac425 100644
--- a/.github/workflows/gradle.yml
+++ b/.github/workflows/gradle.yml
@@ -30,16 +30,6 @@ jobs:
with:
arguments: build
- - name: 테스트 커버리지를 PR에 코멘트로 등록합니다
- id: jacoco
- uses: madrapps/jacoco-report@v1.2
- with:
- title: 📝 테스트 커버리지 리포트입니다
- paths: ${{ github.workspace }}/build/reports/jacoco/test/jacocoTestReport.xml
- token: ${{ secrets.GITHUB_TOKEN }}
- min-coverage-overall: 50
- min-coverage-changed-files: 50
-
# 3. Docker 이미지 빌드
- name: docker image build
run: docker build -t ${{ secrets.DOCKERHUB_USERNAME }}/honeycourses-backend-spring-prod .
diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml
index b19f4c1..2f41cd2 100644
--- a/.github/workflows/tests.yml
+++ b/.github/workflows/tests.yml
@@ -31,3 +31,13 @@ jobs:
if: ${{ always() }} # 테스트가 실패하여도 Report를 보기 위해 `always`로 설정
with:
files: build/test-results/**/*.xml
+
+ - name: 테스트 커버리지를 PR에 코멘트로 등록합니다
+ id: jacoco
+ uses: madrapps/jacoco-report@v1.2
+ with:
+ title: 📝 테스트 커버리지 리포트입니다
+ paths: ${{ github.workspace }}/build/reports/jacoco/test/jacocoTestReport.xml
+ token: ${{ secrets.GITHUB_TOKEN }}
+ min-coverage-overall: 50
+ min-coverage-changed-files: 50
diff --git a/.idea/dataSources.xml b/.idea/dataSources.xml
index 786f792..7ea6678 100644
--- a/.idea/dataSources.xml
+++ b/.idea/dataSources.xml
@@ -27,5 +27,19 @@
$ProjectFileDir$
+
+ mysql.8
+ true
+ true
+ $PROJECT_DIR$/src/main/resources/application.properties
+ com.mysql.cj.jdbc.Driver
+ jdbc:mysql://honeycourses-db.cxpckfpx9yoc.ap-northeast-2.rds.amazonaws.com/railgunT
+
+
+
+
+
+ $ProjectFileDir$
+
\ No newline at end of file
diff --git a/.idea/workspace.xml b/.idea/workspace.xml
index 2473420..645a82f 100644
--- a/.idea/workspace.xml
+++ b/.idea/workspace.xml
@@ -4,9 +4,9 @@
-
+
-
+
@@ -47,9 +47,9 @@
@@ -62,14 +62,12 @@
"assignee": "timingsniper"
}
}
-
-
-
-
-
-
-
-
+ {
+ "selectedUrlAndAccountId": {
+ "url": "https://github.com/WooriVeryGood/honeycourses-backend-spring.git",
+ "accountId": "fa668461-63a5-431a-b8af-cda0dcb3c718"
+ }
+}
{
"isMigrated": true
}
@@ -87,99 +85,136 @@
- {
+ "keyToString": {
+ "Gradle.Build honeycourses-backend-spring.executor": "Run",
+ "Gradle.CommentApiTest.addComment.executor": "Run",
+ "Gradle.CommentControllerTest.addComment.executor": "Run",
+ "Gradle.CommentControllerTest.addReply.executor": "Run",
+ "Gradle.CommentControllerTest.deleteComment.executor": "Run",
+ "Gradle.CommentControllerTest.executor": "Run",
+ "Gradle.CommentControllerTest.findAllCommentsByPostId.executor": "Run",
+ "Gradle.CommentControllerTest.updateComment.executor": "Run",
+ "Gradle.CommentControllerTest.updateComment_exception_noAuth.executor": "Run",
+ "Gradle.CommentControllerTest.updateComment_exception_noContent.executor": "Run",
+ "Gradle.CommentCreateServiceTest.addComment.executor": "Run",
+ "Gradle.CommentCreateServiceTest.addReply_exception_depth.executor": "Run",
+ "Gradle.CommentCreateServiceTest.executor": "Run",
+ "Gradle.CommentDeleteServiceTest.deleteComment_keepChildren.executor": "Run",
+ "Gradle.CommentDeleteServiceTest.deleteReply_exception_noAuth.executor": "Run",
+ "Gradle.CommentDeleteServiceTest.executor": "Run",
+ "Gradle.CommentFindServiceTest.executor": "Coverage",
+ "Gradle.CommentFindServiceTest.findAllCommentsByPostId_cannot_only_reply.executor": "Run",
+ "Gradle.CommentFindServiceTest.findAllCommentsByPostId_softRemoved.executor": "Run",
+ "Gradle.CommentLikeToggleServiceTest.executor": "Run",
+ "Gradle.CommentReportServiceTest.executor": "Run",
+ "Gradle.CommentReportServiceTest.reportComment.executor": "Run",
+ "Gradle.CommentServiceTest.addReply.executor": "Run",
+ "Gradle.CommentServiceTest.addReply_exception_depth.executor": "Run",
+ "Gradle.CommentServiceTest.deletePrentAndReply.executor": "Run",
+ "Gradle.CommentServiceTest.deleteReply.executor": "Run",
+ "Gradle.CommentServiceTest.deleteReply_keep_parent.executor": "Run",
+ "Gradle.CommentServiceTest.executor": "Run",
+ "Gradle.CommentServiceTest.likeComment_up.executor": "Run",
+ "Gradle.CommentServiceTest.updateComment.executor": "Run",
+ "Gradle.CommentServiceTest.updateComment_exception_noAuth.executor": "Run",
+ "Gradle.CommentUpdateServiceTest.executor": "Run",
+ "Gradle.CommentUpdateServiceTest.updateComment.executor": "Run",
+ "Gradle.CourseCreateServiceTest.executor": "Run",
+ "Gradle.CourseServiceTest.executor": "Debug",
+ "Gradle.PostApiTest.executor": "Run",
+ "Gradle.PostApiTest.findPosts.executor": "Run",
+ "Gradle.PostControllerTest.addPost.executor": "Run",
+ "Gradle.PostControllerTest.addPost_exception_noTitle.executor": "Run",
+ "Gradle.PostControllerTest.deletePost.executor": "Run",
+ "Gradle.PostControllerTest.executor": "Run",
+ "Gradle.PostControllerTest.findAllPosts.executor": "Run",
+ "Gradle.PostControllerTest.findMyPosts.executor": "Run",
+ "Gradle.PostControllerTest.findPost.executor": "Run",
+ "Gradle.PostControllerTest.findPost_exception_invalidId.executor": "Run",
+ "Gradle.PostControllerTest.findPosts.executor": "Run",
+ "Gradle.PostControllerTest.findPostsByCategory.executor": "Run",
+ "Gradle.PostControllerTest.likePost_up.executor": "Run",
+ "Gradle.PostControllerTest.updatePost.executor": "Run",
+ "Gradle.PostCreateServiceTest.executor": "Run",
+ "Gradle.PostDeleteServiceTest.deletePost_exception_noAuth.executor": "Run",
+ "Gradle.PostDeleteServiceTest.executor": "Run",
+ "Gradle.PostFindMockTest.executor": "Run",
+ "Gradle.PostFindServiceTest.executor": "Run",
+ "Gradle.PostLikeToggleServiceTest.executor": "Run",
+ "Gradle.PostReportServiceTest.executor": "Run",
+ "Gradle.PostServiceTest.addPost_exception_invalid_category.executor": "Run",
+ "Gradle.PostServiceTest.deletePost.executor": "Run",
+ "Gradle.PostServiceTest.executor": "Run",
+ "Gradle.PostServiceTest.findAllPosts_login.executor": "Run",
+ "Gradle.PostServiceTest.findMyPosts.executor": "Run",
+ "Gradle.PostServiceTest.findPosts_category_free.executor": "Run",
+ "Gradle.PostServiceTest.likePost.executor": "Run",
+ "Gradle.PostServiceTest.likePost_down.executor": "Run",
+ "Gradle.PostServiceTest.likePost_up.executor": "Debug",
+ "Gradle.PostServiceTest.updatePost.executor": "Run",
+ "Gradle.ReportApiTest.executor": "Run",
+ "Gradle.ReportControllerTest.reportComment.executor": "Run",
+ "Gradle.ReportControllerTest.reportPost (1).executor": "Run",
+ "Gradle.ReportControllerTest.reportPost.executor": "Run",
+ "Gradle.ReportServiceTest.reportComment.executor": "Run",
+ "Gradle.ReportServiceTest.reportComment_exception_duplicated.executor": "Run",
+ "Gradle.ReportServiceTest.reportPost.executor": "Run",
+ "Gradle.ReportServiceTest.reportPost_exception_duplicated.executor": "Run",
+ "Gradle.ReportServiceTest.reportPost_exception_invalidId.executor": "Run",
+ "Gradle.ReviewAccessCheckServiceTest.executor": "Run",
+ "Gradle.ReviewApiTest.addReview.executor": "Run",
+ "Gradle.ReviewApiTest.executor": "Run",
+ "Gradle.ReviewApiTest.updateReview.executor": "Run",
+ "Gradle.ReviewControllerTest.findAllReviewsByCourseId_exception_accessDenied.executor": "Run",
+ "Gradle.ReviewControllerTest.findAllReviewsByCourseId_success.executor": "Run",
+ "Gradle.ReviewCreateServiceTest.executor": "Run",
+ "Gradle.ReviewDeleteServiceTest.executor": "Run",
+ "Gradle.ReviewFindServiceTest.executor": "Run",
+ "Gradle.ReviewLikeToggleServiceTest.executor": "Run",
+ "Gradle.ReviewServiceTest.canAccessReviews_false_noReview.executor": "Run",
+ "Gradle.ReviewServiceTest.canAccessReviews_false_sixMonths.executor": "Run",
+ "Gradle.ReviewServiceTest.canAccessReviews_true.executor": "Run",
+ "Gradle.ReviewUpdateServiceTest.executor": "Run",
+ "Gradle.ReviewUpdateServiceTest.updateReview.executor": "Run",
+ "Gradle.ReviewValidateAccessServiceTest.executor": "Run",
+ "Notification.DisplayName-DoNotAsk-Lombok plugin": "Lombok integration problem",
+ "Notification.DoNotAsk-Lombok plugin": "true",
+ "RequestMappingsPanelOrder0": "0",
+ "RequestMappingsPanelOrder1": "1",
+ "RequestMappingsPanelWidth0": "75",
+ "RequestMappingsPanelWidth1": "75",
+ "RunOnceActivity.OpenProjectViewOnStart": "true",
+ "RunOnceActivity.ShowReadmeOnStart": "true",
+ "SHARE_PROJECT_CONFIGURATION_FILES": "true",
+ "Shell Script.gradlew.executor": "Run",
+ "Spring Boot.HoneycoursesBackend.executor": "Run",
+ "WebServerToolWindowFactoryState": "false",
+ "com.intellij.testIntegration.createTest.CreateTestDialog.defaultLibrary": "JUnit5",
+ "com.intellij.testIntegration.createTest.CreateTestDialog.defaultLibrarySuperClass.JUnit5": "",
+ "create.test.in.the.same.root": "true",
+ "extract.method.default.visibility": "private",
+ "git-widget-placeholder": "HC-204-refactoring",
+ "ignore.virus.scanning.warn.message": "true",
+ "kotlin-language-version-configured": "true",
+ "last_opened_file_path": "/Users/pagh/Desktop/honeycourses-backend-spring/build.gradle",
+ "node.js.detected.package.eslint": "true",
+ "node.js.detected.package.tslint": "true",
+ "node.js.selected.package.eslint": "(autodetect)",
+ "node.js.selected.package.tslint": "(autodetect)",
+ "nodejs_package_manager_path": "npm",
+ "project.structure.last.edited": "Modules",
+ "project.structure.proportion": "0.15",
+ "project.structure.side.proportion": "0.2",
+ "settings.editor.selected.configurable": "reference.settingsdialog.project.gradle",
+ "vue.rearranger.settings.migration": "true"
},
- "keyToStringList": {
- "DatabaseDriversLRU": [
- "mysql"
+ "keyToStringList": {
+ "DatabaseDriversLRU": [
+ "mysql"
]
}
-}]]>
+}
@@ -192,13 +227,15 @@
-
-
-
+
+
+
+
+
-
-
+
+
@@ -211,7 +248,7 @@
-
+
@@ -222,7 +259,7 @@
true
-
+
@@ -235,21 +272,18 @@
-
+
false
true
-
-
-
false
true
-
+
@@ -262,7 +296,7 @@
-
+
@@ -273,7 +307,7 @@
true
-
+
@@ -286,7 +320,7 @@
-
+
@@ -297,7 +331,7 @@
true
-
+
@@ -310,7 +344,7 @@
-
+
@@ -329,27 +363,28 @@
-
-
-
-
-
+
+
+
+
+
-
-
-
-
-
+
+
+
+
+
-
+
+
@@ -367,30 +402,24 @@
-
-
-
- 1701334341640
-
-
-
- 1701334341640
-
-
-
- 1701334816668
-
-
-
- 1701334816668
-
-
-
- 1701338098358
-
-
-
- 1701338098358
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
@@ -632,7 +661,159 @@
1709229463130
-
+
+
+ 1709282198941
+
+
+
+ 1709282198941
+
+
+
+ 1709282452134
+
+
+
+ 1709282452134
+
+
+
+ 1709282649153
+
+
+
+ 1709282649153
+
+
+
+ 1709299257142
+
+
+
+ 1709299257142
+
+
+
+ 1709299454208
+
+
+
+ 1709299454208
+
+
+
+ 1709300482047
+
+
+
+ 1709300482047
+
+
+
+ 1711903625412
+
+
+
+ 1711903625412
+
+
+
+ 1711903922175
+
+
+
+ 1711903922175
+
+
+
+ 1711904740106
+
+
+
+ 1711904740106
+
+
+
+ 1711905100699
+
+
+
+ 1711905100699
+
+
+
+ 1711906202974
+
+
+
+ 1711906202974
+
+
+
+ 1711913670243
+
+
+
+ 1711913670243
+
+
+
+ 1711991704464
+
+
+
+ 1711991704465
+
+
+
+ 1711993477793
+
+
+
+ 1711993477793
+
+
+
+ 1711995550289
+
+
+
+ 1711995550289
+
+
+
+ 1712045023435
+
+
+
+ 1712045023435
+
+
+
+ 1712047231893
+
+
+
+ 1712047231893
+
+
+
+ 1712069194844
+
+
+
+ 1712069194844
+
+
+
+ 1712069384199
+
+
+
+ 1712069384199
+
+
@@ -650,23 +831,6 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
@@ -674,8 +838,25 @@
+
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
@@ -689,6 +870,8 @@
+
+
\ No newline at end of file
diff --git a/build.gradle b/build.gradle
index 74487ff..3066ef2 100644
--- a/build.gradle
+++ b/build.gradle
@@ -104,7 +104,7 @@ jacocoTestCoverageVerification {
minimum = 0.75
}
- excludes = ['*.*Controller', '*.advice.*', '*.dto.*', '*.config.*', '*.domain.*', '*.support.*']
+ excludes = ['*.*Controller', '*.advice.*', '*.dto.*', '*.global.*', '*.domain.*']
}
}
}
\ No newline at end of file
diff --git a/src/docs/asciidoc/index.adoc b/src/docs/asciidoc/index.adoc
index 84f3c83..c6416eb 100644
--- a/src/docs/asciidoc/index.adoc
+++ b/src/docs/asciidoc/index.adoc
@@ -29,22 +29,20 @@ operation::reviews/find/success[snippets='http-request,http-response']
==== 실패
===== 최근 작성한 리뷰가 6개월 이상이거나, 없을경우
operation::reviews/find/fail/denied[snippets='http-request,http-response']
-===== 수업이 존재하지 않을 경우
-operation::reviews/find/fail/noCourse[snippets='http-request,http-response']
=== 특정 강의의 리뷰 작성하기 (POST /courses/{id}/reviews)
==== 성공
operation::reviews/create/success[snippets='http-request,http-response']
-=== 리뷰 좋아요 (PUT /courses/reviews/{rid})
+=== 리뷰 좋아요 (PUT /reviews/{id}/like)
==== 성공
operation::reviews/like/success[snippets='http-request,http-response']
-=== 내 리뷰 보기 (GET /courses/reviews/me)
+=== 내 리뷰 보기 (GET /reviews/me)
==== 성공
operation::reviews/find/me/success[snippets='http-request,http-response']
-=== 리뷰 수정 (PUT /courses/reviews/{rid})
+=== 리뷰 수정 (PUT /reviews/{id})
==== 성공
operation::reviews/update/success[snippets='http-request,http-response']
@@ -52,7 +50,7 @@ operation::reviews/update/success[snippets='http-request,http-response']
===== 권한이 없는 경우
operation::reviews/update/fail/noAuth[snippets='http-request,http-response']
-=== 리뷰 삭제 (DELETE /courses/reviews/{rid})
+=== 리뷰 삭제 (DELETE /reviews/{id})
==== 성공
operation::reviews/delete/success[snippets='http-request,http-response']
@@ -62,36 +60,36 @@ operation::reviews/delete/fail/noAuth[snippets='http-request,http-response']
== 게시글 관리
-=== 모든 게시글 보기 (GET /community)
+=== 모든 게시글 보기 (GET /posts)
==== 성공
operation::post/find/all/success[snippets='http-request,http-response']
-=== 특정 카테고리 게시글 보기 (GET /community/category/{category})
-유효한 category 값: free(자유), question(질문), trade(중고거래), offer(구인)
+=== 특정 카테고리 게시글 보기 (GET /posts?page=1&category=free)
+유효한 category 값: free(자유), question(질문), trade(중고거래), offer(구인), notice(공지)
==== 성공
operation::post/find/category/success[snippets='http-request,http-response']
-=== 특정 게시글 보기 (GET /community/{id})
+=== 특정 게시글 보기 (GET /posts/{id})
==== 성공
operation::post/find/one/success[snippets='http-request,http-response']
-=== 게시글 작성하기 (POST /community)
+=== 게시글 작성하기 (POST /posts)
==== 성공
operation::post/create/success[snippets='http-request,http-response']
==== 실패
===== 제목에 내용이 없는 경우
operation::post/create/fail/noTitle[snippets='http-request,http-response']
-=== 내 게시글 보기 (GET /community/me)
+=== 내 게시글 보기 (GET /posts/me)
==== 성공
operation::post/find/me/success[snippets='http-request,http-response']
-=== 게시글 좋아요 (PUT /community/{id}/like)
+=== 게시글 좋아요 (PUT /posts/{id}/like)
==== 성공
operation::post/like/success[snippets='http-request,http-response']
-=== 게시글 수정 (PUT /community/{id})
+=== 게시글 수정 (PUT /posts/{id})
==== 성공
operation::post/update/success[snippets='http-request,http-response']
==== 실패
@@ -100,7 +98,7 @@ operation::post/update/fail/noAuth[snippets='http-request,http-response']
===== 제목에 내용이 없는 경우
operation::post/update/fail/noTitle[snippets='http-request,http-response']
-=== 게시글 삭제 (DELETE /community/{id})
+=== 게시글 삭제 (DELETE /posts/{id})
==== 성공
operation::post/delete/success[snippets='http-request,http-response']
==== 실패
diff --git a/src/main/java/org/wooriverygood/api/SampleApi.java b/src/main/java/org/wooriverygood/api/SampleApi.java
new file mode 100644
index 0000000..ab55589
--- /dev/null
+++ b/src/main/java/org/wooriverygood/api/SampleApi.java
@@ -0,0 +1,15 @@
+package org.wooriverygood.api;
+
+import org.springframework.http.ResponseEntity;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.RestController;
+
+@RestController
+public class SampleApi {
+
+ @GetMapping("/")
+ public ResponseEntity sample() {
+ return ResponseEntity.ok("api is online");
+ }
+
+}
diff --git a/src/main/java/org/wooriverygood/api/advice/ControllerAdvice.java b/src/main/java/org/wooriverygood/api/advice/ControllerAdvice.java
deleted file mode 100644
index 628a008..0000000
--- a/src/main/java/org/wooriverygood/api/advice/ControllerAdvice.java
+++ /dev/null
@@ -1,42 +0,0 @@
-package org.wooriverygood.api.advice;
-
-import org.springframework.http.HttpStatus;
-import org.springframework.http.ResponseEntity;
-import org.springframework.validation.BindingResult;
-import org.springframework.web.bind.MethodArgumentNotValidException;
-import org.springframework.web.bind.annotation.ExceptionHandler;
-import org.springframework.web.bind.annotation.RestControllerAdvice;
-import org.wooriverygood.api.advice.exception.general.BadRequestException;
-import org.wooriverygood.api.advice.exception.general.ForbiddenException;
-import org.wooriverygood.api.advice.exception.general.NotFoundException;
-
-@RestControllerAdvice
-public class ControllerAdvice {
-
- @ExceptionHandler(ForbiddenException.class)
- public ResponseEntity handleForbiddenException(ForbiddenException e) {
- return ResponseEntity.status(HttpStatus.FORBIDDEN)
- .body(new ErrorResponse(e.getMessage()));
- }
-
- @ExceptionHandler(NotFoundException.class)
- public ResponseEntity handleNotFoundException(NotFoundException e) {
- return ResponseEntity.status(HttpStatus.NOT_FOUND)
- .body(new ErrorResponse(e.getMessage()));
- }
-
- @ExceptionHandler(BadRequestException.class)
- public ResponseEntity handleBadRequestException(BadRequestException e) {
- return ResponseEntity.status(HttpStatus.BAD_REQUEST)
- .body(new ErrorResponse(e.getMessage()));
- }
-
- @ExceptionHandler(MethodArgumentNotValidException.class)
- public ResponseEntity handleMethodArgumentNotValidException(BindingResult bindingResult) {
- String message = bindingResult.getFieldErrors()
- .get(0).
- getDefaultMessage();
- return ResponseEntity.status(HttpStatus.BAD_REQUEST)
- .body(new ErrorResponse(message));
- }
-}
diff --git a/src/main/java/org/wooriverygood/api/advice/ErrorResponse.java b/src/main/java/org/wooriverygood/api/advice/ErrorResponse.java
deleted file mode 100644
index a37edb6..0000000
--- a/src/main/java/org/wooriverygood/api/advice/ErrorResponse.java
+++ /dev/null
@@ -1,13 +0,0 @@
-package org.wooriverygood.api.advice;
-
-import lombok.Getter;
-
-@Getter
-public class ErrorResponse {
-
- private final String message;
-
- public ErrorResponse(String message) {
- this.message = message;
- }
-}
diff --git a/src/main/java/org/wooriverygood/api/comment/api/CommentApi.java b/src/main/java/org/wooriverygood/api/comment/api/CommentApi.java
new file mode 100644
index 0000000..ddcdee2
--- /dev/null
+++ b/src/main/java/org/wooriverygood/api/comment/api/CommentApi.java
@@ -0,0 +1,73 @@
+package org.wooriverygood.api.comment.api;
+
+import jakarta.validation.Valid;
+import lombok.RequiredArgsConstructor;
+import org.springframework.http.HttpStatus;
+import org.springframework.http.ResponseEntity;
+import org.springframework.web.bind.annotation.*;
+import org.wooriverygood.api.comment.application.*;
+import org.wooriverygood.api.comment.dto.*;
+import org.wooriverygood.api.global.auth.AuthInfo;
+import org.wooriverygood.api.global.auth.Login;
+
+@RestController
+@RequiredArgsConstructor
+public class CommentApi {
+
+ private final CommentLikeToggleService commentLikeToggleService;
+
+ private final CommentFindService commentFindService;
+
+ private final CommentCreateService commentCreateService;
+
+ private final CommentDeleteService commentDeleteService;
+
+ private final CommentUpdateService commentUpdateService;
+
+
+ @GetMapping("/posts/{id}/comments")
+ public ResponseEntity findAllCommentsByPostId(@PathVariable("id") Long postId,
+ @Login AuthInfo authInfo) {
+ CommentsResponse response = commentFindService.findAllCommentsByPostId(postId, authInfo);
+ return ResponseEntity.ok(response);
+ }
+
+ @PostMapping("/posts/{id}/comments")
+ public ResponseEntity addComment(@PathVariable("id") Long postId,
+ @Login AuthInfo authInfo,
+ @Valid @RequestBody NewCommentRequest newCommentRequest) {
+ commentCreateService.addComment(authInfo, postId, newCommentRequest);
+ return ResponseEntity.status(HttpStatus.CREATED).build();
+ }
+
+ @PutMapping("/comments/{id}/like")
+ public ResponseEntity toggleCommentLike(@PathVariable("id") Long commentId,
+ @Login AuthInfo authInfo) {
+ CommentLikeResponse response = commentLikeToggleService.likeComment(commentId, authInfo);
+ return ResponseEntity.ok(response);
+ }
+
+ @PutMapping("/comments/{id}")
+ public ResponseEntity updateComment(@PathVariable("id") Long commentId,
+ @Valid @RequestBody CommentUpdateRequest request,
+ @Login AuthInfo authInfo) {
+ commentUpdateService.updateComment(commentId, request, authInfo);
+ return ResponseEntity.noContent().build();
+ }
+
+ @DeleteMapping("/comments/{id}")
+ public ResponseEntity deleteComment(@PathVariable("id") Long commentId,
+ @Login AuthInfo authInfo) {
+ commentDeleteService.deleteComment(commentId, authInfo);
+ return ResponseEntity.noContent().build();
+ }
+
+ @PostMapping("/comments/{id}/reply")
+ public ResponseEntity addReply(@PathVariable("id") Long commentId,
+ @RequestBody NewReplyRequest request,
+ @Login AuthInfo authInfo) {
+ commentCreateService.addReply(commentId, request, authInfo);
+ return ResponseEntity.status(HttpStatus.CREATED).build();
+ }
+
+}
diff --git a/src/main/java/org/wooriverygood/api/comment/application/CommentCreateService.java b/src/main/java/org/wooriverygood/api/comment/application/CommentCreateService.java
new file mode 100644
index 0000000..60c338e
--- /dev/null
+++ b/src/main/java/org/wooriverygood/api/comment/application/CommentCreateService.java
@@ -0,0 +1,68 @@
+package org.wooriverygood.api.comment.application;
+
+import lombok.RequiredArgsConstructor;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+import org.wooriverygood.api.comment.exception.CommentNotFoundException;
+import org.wooriverygood.api.comment.exception.ReplyDepthException;
+import org.wooriverygood.api.comment.domain.Comment;
+import org.wooriverygood.api.comment.dto.NewCommentRequest;
+import org.wooriverygood.api.comment.dto.NewReplyRequest;
+import org.wooriverygood.api.comment.repository.CommentRepository;
+import org.wooriverygood.api.global.auth.AuthInfo;
+import org.wooriverygood.api.member.domain.Member;
+import org.wooriverygood.api.member.exception.MemberNotFoundException;
+import org.wooriverygood.api.member.repository.MemberRepository;
+import org.wooriverygood.api.post.domain.Post;
+import org.wooriverygood.api.post.exception.PostNotFoundException;
+import org.wooriverygood.api.post.repository.PostRepository;
+
+@Service
+@Transactional
+@RequiredArgsConstructor
+public class CommentCreateService {
+
+ private final PostRepository postRepository;
+
+ private final CommentRepository commentRepository;
+
+ private final MemberRepository memberRepository;
+
+
+ public void addComment(AuthInfo authInfo, Long postId, NewCommentRequest request) {
+ Member member = memberRepository.findById(authInfo.getMemberId())
+ .orElseThrow(MemberNotFoundException::new);
+ Post post = postRepository.findById(postId)
+ .orElseThrow(PostNotFoundException::new);
+
+ Comment comment = Comment.builder()
+ .content(request.getContent())
+ .post(post)
+ .member(member)
+ .build();
+
+ commentRepository.save(comment);
+ }
+
+ public void addReply(Long commentId, NewReplyRequest request, AuthInfo authInfo) {
+ Comment parent = commentRepository.findById(commentId)
+ .orElseThrow(CommentNotFoundException::new);
+
+ if (!parent.isParent())
+ throw new ReplyDepthException();
+
+ Member member = memberRepository.findById(authInfo.getMemberId())
+ .orElseThrow(MemberNotFoundException::new);
+
+ Comment child = Comment.builder()
+ .content(request.getContent())
+ .post(parent.getPost())
+ .parent(parent)
+ .member(member)
+ .build();
+ parent.addReply(child);
+
+ commentRepository.save(child);
+ }
+
+}
diff --git a/src/main/java/org/wooriverygood/api/comment/application/CommentDeleteService.java b/src/main/java/org/wooriverygood/api/comment/application/CommentDeleteService.java
new file mode 100644
index 0000000..4bab334
--- /dev/null
+++ b/src/main/java/org/wooriverygood/api/comment/application/CommentDeleteService.java
@@ -0,0 +1,64 @@
+package org.wooriverygood.api.comment.application;
+
+import lombok.RequiredArgsConstructor;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+import org.wooriverygood.api.comment.exception.CommentNotFoundException;
+import org.wooriverygood.api.comment.domain.Comment;
+import org.wooriverygood.api.comment.repository.CommentLikeRepository;
+import org.wooriverygood.api.comment.repository.CommentRepository;
+import org.wooriverygood.api.global.auth.AuthInfo;
+import org.wooriverygood.api.member.domain.Member;
+import org.wooriverygood.api.member.exception.MemberNotFoundException;
+import org.wooriverygood.api.member.repository.MemberRepository;
+
+@Service
+@Transactional
+@RequiredArgsConstructor
+public class CommentDeleteService {
+
+ private final CommentRepository commentRepository;
+
+ private final CommentLikeRepository commentLikeRepository;
+
+ private final MemberRepository memberRepository;
+
+
+ public void deleteComment(Long commentId, AuthInfo authInfo) {
+ Member member = memberRepository.findById(authInfo.getMemberId())
+ .orElseThrow(MemberNotFoundException::new);
+ Comment comment = commentRepository.findById(commentId)
+ .orElseThrow(CommentNotFoundException::new);
+
+ comment.validateAuthor(member);
+
+ commentLikeRepository.deleteAllByComment(comment);
+ deleteCommentOrReply(comment);
+ }
+
+ private void deleteCommentOrReply(Comment comment) {
+ if (comment.isParent()) {
+ deleteParent(comment);
+ return;
+ }
+ deleteReply(comment);
+ }
+
+ private void deleteParent(Comment parent) {
+ if (parent.hasNoReply()) {
+ commentRepository.delete(parent);
+ return;
+ }
+ parent.willBeDeleted();
+ }
+
+ private void deleteReply(Comment reply) {
+ Comment parent = reply.getParent();
+ parent.deleteReply(reply);
+ commentRepository.delete(reply);
+
+ if (parent.canDelete())
+ commentRepository.delete(parent);
+ }
+
+}
diff --git a/src/main/java/org/wooriverygood/api/comment/application/CommentFindService.java b/src/main/java/org/wooriverygood/api/comment/application/CommentFindService.java
new file mode 100644
index 0000000..599a19a
--- /dev/null
+++ b/src/main/java/org/wooriverygood/api/comment/application/CommentFindService.java
@@ -0,0 +1,63 @@
+package org.wooriverygood.api.comment.application;
+
+import lombok.RequiredArgsConstructor;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+import org.wooriverygood.api.comment.domain.Comment;
+import org.wooriverygood.api.comment.dto.CommentResponse;
+import org.wooriverygood.api.comment.dto.CommentsResponse;
+import org.wooriverygood.api.comment.dto.ReplyResponse;
+import org.wooriverygood.api.comment.repository.CommentLikeRepository;
+import org.wooriverygood.api.comment.repository.CommentRepository;
+import org.wooriverygood.api.global.auth.AuthInfo;
+import org.wooriverygood.api.member.domain.Member;
+import org.wooriverygood.api.member.exception.MemberNotFoundException;
+import org.wooriverygood.api.member.repository.MemberRepository;
+
+import java.util.List;
+import java.util.Objects;
+
+@Service
+@RequiredArgsConstructor
+@Transactional(readOnly = true)
+public class CommentFindService {
+
+ private final CommentRepository commentRepository;
+
+ private final CommentLikeRepository commentLikeRepository;
+
+ private final MemberRepository memberRepository;
+
+
+ public CommentsResponse findAllCommentsByPostId(Long postId, AuthInfo authInfo) {
+ Member member = memberRepository.findById(authInfo.getMemberId())
+ .orElseThrow(MemberNotFoundException::new);
+ List comments = commentRepository.findAllByPostId(postId);
+ List responses = comments.stream()
+ .map(comment -> convertToCommentResponse(comment, member))
+ .filter(response -> !Objects.isNull(response))
+ .toList();
+ return new CommentsResponse(responses);
+ }
+
+ private CommentResponse convertToCommentResponse(Comment comment, Member member) {
+ if (comment.isReply())
+ return null;
+ if (comment.isSoftRemoved())
+ return CommentResponse.softRemovedOf(comment, convertToReplyResponses(comment, member), comment.sameAuthor(member));
+
+ boolean liked = commentLikeRepository.existsByCommentAndMember(comment, member);
+ return CommentResponse.of(comment, convertToReplyResponses(comment, member), liked, comment.sameAuthor(member));
+ }
+
+ private List convertToReplyResponses(Comment parent, Member member) {
+ return parent.getReplies()
+ .stream()
+ .map(reply -> {
+ boolean liked = commentLikeRepository.existsByCommentAndMember(reply, member);
+ return ReplyResponse.of(reply, liked, reply.sameAuthor(member));
+ })
+ .toList();
+ }
+
+}
diff --git a/src/main/java/org/wooriverygood/api/comment/application/CommentLikeToggleService.java b/src/main/java/org/wooriverygood/api/comment/application/CommentLikeToggleService.java
new file mode 100644
index 0000000..e05aef3
--- /dev/null
+++ b/src/main/java/org/wooriverygood/api/comment/application/CommentLikeToggleService.java
@@ -0,0 +1,72 @@
+package org.wooriverygood.api.comment.application;
+
+import lombok.RequiredArgsConstructor;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+import org.wooriverygood.api.comment.domain.Comment;
+import org.wooriverygood.api.comment.domain.CommentLike;
+import org.wooriverygood.api.comment.dto.*;
+import org.wooriverygood.api.comment.repository.CommentLikeRepository;
+import org.wooriverygood.api.comment.repository.CommentRepository;
+import org.wooriverygood.api.comment.exception.CommentNotFoundException;
+import org.wooriverygood.api.global.auth.AuthInfo;
+import org.wooriverygood.api.member.domain.Member;
+import org.wooriverygood.api.member.exception.MemberNotFoundException;
+import org.wooriverygood.api.member.repository.MemberRepository;
+
+import java.util.Optional;
+
+@Service
+@Transactional
+@RequiredArgsConstructor
+public class CommentLikeToggleService {
+
+ private final CommentRepository commentRepository;
+
+ private final CommentLikeRepository commentLikeRepository;
+
+ private final MemberRepository memberRepository;
+
+
+ public CommentLikeResponse likeComment(Long commentId, AuthInfo authInfo) {
+ Member member = memberRepository.findById(authInfo.getMemberId())
+ .orElseThrow(MemberNotFoundException::new);
+ Comment comment = commentRepository.findById(commentId)
+ .orElseThrow(CommentNotFoundException::new);
+
+ Optional commentLike = commentLikeRepository.findByCommentAndMember(comment, member);
+
+ if (commentLike.isEmpty()) {
+ addCommentLike(comment, member);
+ return createCommentLikeResponse(comment, true);
+ }
+
+ deleteCommentLike(comment, commentLike.get());
+ return createCommentLikeResponse(comment, false);
+ }
+
+ private void addCommentLike(Comment comment, Member member) {
+ CommentLike commentLike = CommentLike.builder()
+ .comment(comment)
+ .member(member)
+ .build();
+
+ comment.addCommentLike(commentLike);
+ commentLikeRepository.save(commentLike);
+ commentRepository.increaseLikeCount(comment.getId());
+ }
+
+ private void deleteCommentLike(Comment comment, CommentLike commentLike) {
+ comment.deleteCommentLike(commentLike);
+ commentRepository.decreaseLikeCount(comment.getId());
+ }
+
+ private CommentLikeResponse createCommentLikeResponse(Comment comment, boolean liked) {
+ int likeCount = comment.getLikeCount() + (liked ? 1 : -1);
+ return CommentLikeResponse.builder()
+ .likeCount(likeCount)
+ .liked(liked)
+ .build();
+ }
+
+}
diff --git a/src/main/java/org/wooriverygood/api/comment/application/CommentUpdateService.java b/src/main/java/org/wooriverygood/api/comment/application/CommentUpdateService.java
new file mode 100644
index 0000000..20e8e4a
--- /dev/null
+++ b/src/main/java/org/wooriverygood/api/comment/application/CommentUpdateService.java
@@ -0,0 +1,36 @@
+package org.wooriverygood.api.comment.application;
+
+import lombok.RequiredArgsConstructor;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+import org.wooriverygood.api.comment.exception.CommentNotFoundException;
+import org.wooriverygood.api.comment.domain.Comment;
+import org.wooriverygood.api.comment.dto.CommentUpdateRequest;
+import org.wooriverygood.api.comment.repository.CommentRepository;
+import org.wooriverygood.api.global.auth.AuthInfo;
+import org.wooriverygood.api.member.domain.Member;
+import org.wooriverygood.api.member.exception.MemberNotFoundException;
+import org.wooriverygood.api.member.repository.MemberRepository;
+
+@Service
+@Transactional
+@RequiredArgsConstructor
+public class CommentUpdateService {
+
+ private final CommentRepository commentRepository;
+
+ private final MemberRepository memberRepository;
+
+
+ public void updateComment(Long commentId, CommentUpdateRequest request, AuthInfo authInfo) {
+ Member member = memberRepository.findById(authInfo.getMemberId())
+ .orElseThrow(MemberNotFoundException::new);
+ Comment comment = commentRepository.findById(commentId)
+ .orElseThrow(CommentNotFoundException::new);
+
+ comment.validateAuthor(member);
+
+ comment.updateContent(request.getContent());
+ }
+
+}
diff --git a/src/main/java/org/wooriverygood/api/comment/controller/CommentController.java b/src/main/java/org/wooriverygood/api/comment/controller/CommentController.java
deleted file mode 100644
index b85c74c..0000000
--- a/src/main/java/org/wooriverygood/api/comment/controller/CommentController.java
+++ /dev/null
@@ -1,69 +0,0 @@
-package org.wooriverygood.api.comment.controller;
-
-import jakarta.validation.Valid;
-import org.springframework.http.HttpStatus;
-import org.springframework.http.ResponseEntity;
-import org.springframework.web.bind.annotation.*;
-import org.wooriverygood.api.comment.dto.*;
-import org.wooriverygood.api.comment.service.CommentService;
-import org.wooriverygood.api.support.AuthInfo;
-import org.wooriverygood.api.support.Login;
-
-import java.util.List;
-
-@RestController
-public class CommentController {
-
- private final CommentService commentService;
-
-
- public CommentController(CommentService commentService) {
- this.commentService = commentService;
- }
-
- @GetMapping("/community/{id}/comments")
- public ResponseEntity> findAllCommentsByPostId(@PathVariable("id") Long postId,
- @Login AuthInfo authInfo) {
- List responses = commentService.findAllComments(postId, authInfo);
- return ResponseEntity.ok(responses);
- }
-
- @PostMapping("/community/{id}/comments")
- public ResponseEntity addComment(@PathVariable("id") Long postId,
- @Login AuthInfo authInfo,
- @Valid @RequestBody NewCommentRequest newCommentRequest) {
- NewCommentResponse response = commentService.addComment(authInfo, postId, newCommentRequest);
- return ResponseEntity.status(HttpStatus.CREATED).body(response);
- }
-
- @PutMapping("/comments/{id}/like")
- public ResponseEntity likeComment(@PathVariable("id") Long commentId,
- @Login AuthInfo authInfo) {
- CommentLikeResponse response = commentService.likeComment(commentId, authInfo);
- return ResponseEntity.ok(response);
- }
-
- @PutMapping("/comments/{id}")
- public ResponseEntity updateComment(@PathVariable("id") Long commentId,
- @Valid @RequestBody CommentUpdateRequest request,
- @Login AuthInfo authInfo) {
- CommentUpdateResponse response = commentService.updateComment(commentId, request, authInfo);
- return ResponseEntity.ok(response);
- }
-
- @DeleteMapping("/comments/{id}")
- public ResponseEntity deleteComment(@PathVariable("id") Long commentId,
- @Login AuthInfo authInfo) {
- CommentDeleteResponse response = commentService.deleteComment(commentId, authInfo);
- return ResponseEntity.ok(response);
- }
-
- @PostMapping("/comments/{id}/reply")
- public ResponseEntity addReply(@PathVariable("id") Long commentId,
- @RequestBody NewReplyRequest request,
- @Login AuthInfo authInfo) {
- commentService.addReply(commentId, request, authInfo);
- return ResponseEntity.status(HttpStatus.CREATED).build();
- }
-
-}
diff --git a/src/main/java/org/wooriverygood/api/comment/domain/Comment.java b/src/main/java/org/wooriverygood/api/comment/domain/Comment.java
index 0d43326..d5a4b0f 100644
--- a/src/main/java/org/wooriverygood/api/comment/domain/Comment.java
+++ b/src/main/java/org/wooriverygood/api/comment/domain/Comment.java
@@ -1,13 +1,12 @@
package org.wooriverygood.api.comment.domain;
import jakarta.persistence.*;
-import lombok.Builder;
-import lombok.Getter;
-import lombok.NoArgsConstructor;
+import lombok.*;
import org.hibernate.annotations.ColumnDefault;
import org.springframework.data.annotation.CreatedDate;
import org.springframework.data.jpa.domain.support.AuditingEntityListener;
-import org.wooriverygood.api.advice.exception.AuthorizationException;
+import org.wooriverygood.api.global.error.exception.AuthorizationException;
+import org.wooriverygood.api.member.domain.Member;
import org.wooriverygood.api.post.domain.Post;
import org.wooriverygood.api.report.domain.CommentReport;
@@ -17,10 +16,10 @@
import java.util.Objects;
@Entity
-@Table(name = "comments")
@Getter
+@Table(name = "comments")
@EntityListeners(AuditingEntityListener.class)
-@NoArgsConstructor
+@NoArgsConstructor(access = AccessLevel.PROTECTED)
public class Comment {
@Id
@@ -33,27 +32,27 @@ public class Comment {
private Comment parent;
@OneToMany(mappedBy = "parent")
- private List children = new ArrayList<>();
+ private List replies = new ArrayList<>();
@Column(name = "comment_content", length = 200, nullable = false)
private String content;
- @Column(name = "comment_author", length = 1000)
- private String author;
+ @ManyToOne(fetch = FetchType.LAZY)
+ private Member member;
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "post_id", referencedColumnName = "post_id")
private Post post;
@OneToMany(mappedBy = "comment", cascade = CascadeType.ALL, orphanRemoval = true)
- private List commentLikes;
+ private List commentLikes = new ArrayList<>();
@Column(name = "like_count")
@ColumnDefault("0")
private int likeCount;
@OneToMany(mappedBy = "comment")
- private List reports;
+ private List reports = new ArrayList<>();
@Column(name = "report_count")
@ColumnDefault("0")
@@ -71,16 +70,18 @@ public class Comment {
private boolean updated;
@Builder
- public Comment(Long id, String content, String author, Post post, Comment parent, List commentLikes, List reports, boolean softRemoved, boolean updated) {
+ public Comment(Long id, String content, Post post, Member member,
+ Comment parent, List commentLikes,
+ List reports, boolean softRemoved, boolean updated) {
this.id = id;
this.content = content;
- this.author = author;
this.post = post;
this.parent = parent;
this.commentLikes = commentLikes;
this.reports = reports;
this.softRemoved = softRemoved;
this.updated = updated;
+ this.member = member;
}
public void addCommentLike(CommentLike commentLike) {
@@ -96,15 +97,19 @@ public void addReport(CommentReport report) {
reports.add(report);
}
- public boolean hasReportByUser(String username) {
+ public boolean hasReportByMember(Member member) {
for (CommentReport report: reports)
- if (report.isOwner(username))
+ if (report.isOwner(member))
return true;
return false;
}
- public void validateAuthor(String author) {
- if (!this.author.equals(author)) throw new AuthorizationException();
+ public boolean sameAuthor(Member member) {
+ return this.member.equals(member);
+ }
+
+ public void validateAuthor(Member member) {
+ if (!sameAuthor(member)) throw new AuthorizationException();
}
public void updateContent(String content) {
@@ -112,12 +117,12 @@ public void updateContent(String content) {
updated = true;
}
- public void addChildren(Comment reply) {
- children.add(reply);
+ public void addReply(Comment reply) {
+ replies.add(reply);
}
- public void deleteChild(Comment reply) {
- children.remove(reply);
+ public void deleteReply(Comment reply) {
+ replies.remove(reply);
reply.delete();
}
@@ -134,7 +139,7 @@ public boolean isReply() {
}
public boolean hasNoReply() {
- return children.isEmpty();
+ return replies.isEmpty();
}
public void willBeDeleted() {
@@ -145,4 +150,12 @@ public boolean canDelete() {
return hasNoReply() && softRemoved;
}
+ public boolean isReportedTooMuch() {
+ return reportCount >= 5;
+ }
+
+ public String getContent() {
+ return isReportedTooMuch() ? null : content;
+ }
+
}
diff --git a/src/main/java/org/wooriverygood/api/comment/domain/CommentLike.java b/src/main/java/org/wooriverygood/api/comment/domain/CommentLike.java
index 320e161..361b595 100644
--- a/src/main/java/org/wooriverygood/api/comment/domain/CommentLike.java
+++ b/src/main/java/org/wooriverygood/api/comment/domain/CommentLike.java
@@ -4,9 +4,10 @@
import lombok.Builder;
import lombok.Getter;
import lombok.NoArgsConstructor;
+import org.wooriverygood.api.member.domain.Member;
@Entity
-@Table(name = "commentLikes")
+@Table(name = "comment_likes")
@Getter
@NoArgsConstructor
public class CommentLike {
@@ -19,15 +20,15 @@ public class CommentLike {
@JoinColumn(name = "comment_id", referencedColumnName = "comment_id")
private Comment comment;
- @Column
- private String username;
+ @ManyToOne(fetch = FetchType.LAZY)
+ private Member member;
@Builder
- public CommentLike(Long id, Comment comment, String username) {
+ public CommentLike(Long id, Comment comment, Member member) {
this.id = id;
this.comment = comment;
- this.username = username;
+ this.member = member;
}
public void delete() {
diff --git a/src/main/java/org/wooriverygood/api/comment/dto/CommentDeleteResponse.java b/src/main/java/org/wooriverygood/api/comment/dto/CommentDeleteResponse.java
deleted file mode 100644
index 9fb453e..0000000
--- a/src/main/java/org/wooriverygood/api/comment/dto/CommentDeleteResponse.java
+++ /dev/null
@@ -1,15 +0,0 @@
-package org.wooriverygood.api.comment.dto;
-
-import lombok.Builder;
-import lombok.Getter;
-
-@Getter
-public class CommentDeleteResponse {
-
- private final Long comment_id;
-
- @Builder
- public CommentDeleteResponse(Long comment_id) {
- this.comment_id = comment_id;
- }
-}
diff --git a/src/main/java/org/wooriverygood/api/comment/dto/CommentLikeResponse.java b/src/main/java/org/wooriverygood/api/comment/dto/CommentLikeResponse.java
index dad11f8..872cdf3 100644
--- a/src/main/java/org/wooriverygood/api/comment/dto/CommentLikeResponse.java
+++ b/src/main/java/org/wooriverygood/api/comment/dto/CommentLikeResponse.java
@@ -1,18 +1,22 @@
package org.wooriverygood.api.comment.dto;
+import com.fasterxml.jackson.databind.PropertyNamingStrategies;
+import com.fasterxml.jackson.databind.annotation.JsonNaming;
import lombok.Builder;
import lombok.Getter;
@Getter
+@JsonNaming(PropertyNamingStrategies.SnakeCaseStrategy.class)
public class CommentLikeResponse {
- private final int like_count;
+ private final int likeCount;
private final boolean liked;
+
@Builder
- public CommentLikeResponse(int like_count, boolean liked) {
- this.like_count = like_count;
+ public CommentLikeResponse(int likeCount, boolean liked) {
+ this.likeCount = likeCount;
this.liked = liked;
}
diff --git a/src/main/java/org/wooriverygood/api/comment/dto/CommentResponse.java b/src/main/java/org/wooriverygood/api/comment/dto/CommentResponse.java
index d8bb30b..d610aa6 100644
--- a/src/main/java/org/wooriverygood/api/comment/dto/CommentResponse.java
+++ b/src/main/java/org/wooriverygood/api/comment/dto/CommentResponse.java
@@ -1,5 +1,7 @@
package org.wooriverygood.api.comment.dto;
+import com.fasterxml.jackson.databind.PropertyNamingStrategies;
+import com.fasterxml.jackson.databind.annotation.JsonNaming;
import lombok.Builder;
import lombok.Getter;
import org.wooriverygood.api.comment.domain.Comment;
@@ -8,19 +10,18 @@
import java.util.List;
@Getter
+@JsonNaming(PropertyNamingStrategies.SnakeCaseStrategy.class)
public class CommentResponse {
- private final Long comment_id;
+ private final Long commentId;
- private final String comment_content;
+ private final String commentContent;
- private final String comment_author;
+ private final Long postId;
- private final Long post_id;
+ private final int commentLikeCount;
- private final int comment_likes;
-
- private final LocalDateTime comment_time;
+ private final LocalDateTime commentTime;
private final boolean liked;
@@ -30,50 +31,61 @@ public class CommentResponse {
private final boolean reported;
+ private final boolean isMine;
+
+ private final long memberId;
+
@Builder
- public CommentResponse(Long comment_id, String comment_content, String comment_author, Long post_id, int comment_likes, LocalDateTime comment_time, boolean liked, List replies, boolean updated, boolean reported) {
- this.comment_id = comment_id;
- this.comment_content = comment_content;
- this.comment_author = comment_author;
- this.post_id = post_id;
- this.comment_likes = comment_likes;
- this.comment_time = comment_time;
+ public CommentResponse(
+ Long commentId, String commentContent, Long postId,
+ int commentLikeCount, LocalDateTime commentTime, boolean liked,
+ List replies, boolean updated, boolean reported,
+ boolean isMine, long memberId
+ ) {
+ this.commentId = commentId;
+ this.commentContent = commentContent;
+ this.postId = postId;
+ this.commentLikeCount = commentLikeCount;
+ this.commentTime = commentTime;
this.liked = liked;
this.replies = replies;
this.updated = updated;
this.reported = reported;
+ this.isMine = isMine;
+ this.memberId = memberId;
}
- public static CommentResponse from(Comment comment, List replies, boolean liked) {
- boolean reported = comment.getReportCount() >= 5;
+ public static CommentResponse of(Comment comment, List replies, boolean liked, boolean isMine) {
return CommentResponse.builder()
- .comment_id(comment.getId())
- .comment_content(reported ? null : comment.getContent())
- .comment_author(comment.getAuthor())
- .post_id(comment.getPost().getId())
- .comment_likes(comment.getLikeCount())
- .comment_time(comment.getCreatedAt())
+ .commentId(comment.getId())
+ .commentContent(comment.getContent())
+ .memberId(comment.getMember().getId())
+ .postId(comment.getPost().getId())
+ .commentLikeCount(comment.getLikeCount())
+ .commentTime(comment.getCreatedAt())
.liked(liked)
.replies(replies)
.updated(comment.isUpdated())
- .reported(reported)
+ .reported(comment.isReportedTooMuch())
+ .isMine(isMine)
.build();
}
- public static CommentResponse softRemovedFrom(Comment comment, List replies) {
- boolean reported = comment.getReportCount() >= 5;
+ public static CommentResponse softRemovedOf(Comment comment, List replies, boolean isMine) {
return CommentResponse.builder()
- .comment_id(comment.getId())
- .comment_content(null)
- .comment_author(comment.getAuthor())
- .post_id(comment.getPost().getId())
- .comment_likes(comment.getLikeCount())
- .comment_time(comment.getCreatedAt())
+ .commentId(comment.getId())
+ .commentContent(null)
+ .memberId(comment.getMember().getId())
+ .postId(comment.getPost().getId())
+ .commentLikeCount(comment.getLikeCount())
+ .commentTime(comment.getCreatedAt())
.replies(replies)
.updated(comment.isUpdated())
- .reported(reported)
+ .reported(comment.isReportedTooMuch())
+ .isMine(isMine)
.build();
}
+
}
diff --git a/src/main/java/org/wooriverygood/api/comment/dto/CommentUpdateResponse.java b/src/main/java/org/wooriverygood/api/comment/dto/CommentUpdateResponse.java
deleted file mode 100644
index bd85f6f..0000000
--- a/src/main/java/org/wooriverygood/api/comment/dto/CommentUpdateResponse.java
+++ /dev/null
@@ -1,15 +0,0 @@
-package org.wooriverygood.api.comment.dto;
-
-import lombok.Builder;
-import lombok.Getter;
-
-@Getter
-public class CommentUpdateResponse {
-
- private final Long comment_id;
-
- @Builder
- public CommentUpdateResponse(Long comment_id) {
- this.comment_id = comment_id;
- }
-}
diff --git a/src/main/java/org/wooriverygood/api/comment/dto/CommentsResponse.java b/src/main/java/org/wooriverygood/api/comment/dto/CommentsResponse.java
new file mode 100644
index 0000000..fa05254
--- /dev/null
+++ b/src/main/java/org/wooriverygood/api/comment/dto/CommentsResponse.java
@@ -0,0 +1,17 @@
+package org.wooriverygood.api.comment.dto;
+
+import lombok.Getter;
+
+import java.util.List;
+
+@Getter
+public class CommentsResponse {
+
+ private final List comments;
+
+
+ public CommentsResponse(List comments) {
+ this.comments = comments;
+ }
+
+}
diff --git a/src/main/java/org/wooriverygood/api/comment/dto/NewCommentResponse.java b/src/main/java/org/wooriverygood/api/comment/dto/NewCommentResponse.java
deleted file mode 100644
index 04c07b1..0000000
--- a/src/main/java/org/wooriverygood/api/comment/dto/NewCommentResponse.java
+++ /dev/null
@@ -1,19 +0,0 @@
-package org.wooriverygood.api.comment.dto;
-
-import lombok.Builder;
-import lombok.Getter;
-
-@Getter
-public class NewCommentResponse {
-
- private Long comment_id;
- private String content;
- private String author;
-
- @Builder
- public NewCommentResponse(Long comment_id, String content, String author) {
- this.comment_id = comment_id;
- this.content = content;
- this.author = author;
- }
-}
diff --git a/src/main/java/org/wooriverygood/api/comment/dto/ReplyResponse.java b/src/main/java/org/wooriverygood/api/comment/dto/ReplyResponse.java
index eda61ed..7556f1a 100644
--- a/src/main/java/org/wooriverygood/api/comment/dto/ReplyResponse.java
+++ b/src/main/java/org/wooriverygood/api/comment/dto/ReplyResponse.java
@@ -1,5 +1,7 @@
package org.wooriverygood.api.comment.dto;
+import com.fasterxml.jackson.databind.PropertyNamingStrategies;
+import com.fasterxml.jackson.databind.annotation.JsonNaming;
import lombok.Builder;
import lombok.Getter;
import org.wooriverygood.api.comment.domain.Comment;
@@ -7,16 +9,16 @@
import java.time.LocalDateTime;
@Getter
+@JsonNaming(PropertyNamingStrategies.SnakeCaseStrategy.class)
public class ReplyResponse {
- private final Long reply_id;
- private final String reply_content;
+ private final Long replyId;
- private final String reply_author;
+ private final String replyContent;
- private final int reply_likes;
+ private final int replyLikeCount;
- private final LocalDateTime reply_time;
+ private final LocalDateTime replyTime;
private final boolean liked;
@@ -24,31 +26,39 @@ public class ReplyResponse {
private final boolean reported;
+ private final boolean isMine;
+
+ private final long memberId;
- @Builder
- public ReplyResponse(Long reply_id, String reply_content, String reply_author, int reply_likes, LocalDateTime reply_time, boolean liked, boolean updated, boolean reported) {
- this.reply_id = reply_id;
- this.reply_content = reply_content;
- this.reply_author = reply_author;
- this.reply_likes = reply_likes;
- this.reply_time = reply_time;
+ @Builder
+ public ReplyResponse(
+ Long replyId, String replyContent, int replyLikeCount,
+ LocalDateTime replyTime, boolean liked, boolean updated,
+ boolean reported, boolean isMine, long memberId
+ ) {
+ this.replyId = replyId;
+ this.replyContent = replyContent;
+ this.isMine = isMine;
+ this.memberId = memberId;
+ this.replyLikeCount = replyLikeCount;
+ this.replyTime = replyTime;
this.liked = liked;
this.updated = updated;
this.reported = reported;
}
- public static ReplyResponse from(Comment reply, boolean liked) {
- boolean reported = reply.getReportCount() >= 5;
+ public static ReplyResponse of(Comment reply, boolean liked, boolean isMine) {
return ReplyResponse.builder()
- .reply_id(reply.getId())
- .reply_content(reported ? null : reply.getContent())
- .reply_author(reply.getAuthor())
- .reply_likes(reply.getLikeCount())
- .reply_time(reply.getCreatedAt())
+ .replyId(reply.getId())
+ .replyContent(reply.getContent())
+ .memberId(reply.getMember().getId())
+ .replyLikeCount(reply.getLikeCount())
+ .replyTime(reply.getCreatedAt())
.liked(liked)
.updated(reply.isUpdated())
- .reported(reported)
+ .reported(reply.isReportedTooMuch())
+ .isMine(isMine)
.build();
}
diff --git a/src/main/java/org/wooriverygood/api/advice/exception/CommentNotFoundException.java b/src/main/java/org/wooriverygood/api/comment/exception/CommentNotFoundException.java
similarity index 64%
rename from src/main/java/org/wooriverygood/api/advice/exception/CommentNotFoundException.java
rename to src/main/java/org/wooriverygood/api/comment/exception/CommentNotFoundException.java
index 83e284a..e45e533 100644
--- a/src/main/java/org/wooriverygood/api/advice/exception/CommentNotFoundException.java
+++ b/src/main/java/org/wooriverygood/api/comment/exception/CommentNotFoundException.java
@@ -1,6 +1,6 @@
-package org.wooriverygood.api.advice.exception;
+package org.wooriverygood.api.comment.exception;
-import org.wooriverygood.api.advice.exception.general.NotFoundException;
+import org.wooriverygood.api.global.error.exception.NotFoundException;
public class CommentNotFoundException extends NotFoundException {
diff --git a/src/main/java/org/wooriverygood/api/advice/exception/ReplyDepthException.java b/src/main/java/org/wooriverygood/api/comment/exception/ReplyDepthException.java
similarity index 64%
rename from src/main/java/org/wooriverygood/api/advice/exception/ReplyDepthException.java
rename to src/main/java/org/wooriverygood/api/comment/exception/ReplyDepthException.java
index af7438a..90aee51 100644
--- a/src/main/java/org/wooriverygood/api/advice/exception/ReplyDepthException.java
+++ b/src/main/java/org/wooriverygood/api/comment/exception/ReplyDepthException.java
@@ -1,6 +1,6 @@
-package org.wooriverygood.api.advice.exception;
+package org.wooriverygood.api.comment.exception;
-import org.wooriverygood.api.advice.exception.general.BadRequestException;
+import org.wooriverygood.api.global.error.exception.BadRequestException;
public class ReplyDepthException extends BadRequestException {
diff --git a/src/main/java/org/wooriverygood/api/comment/repository/CommentLikeRepository.java b/src/main/java/org/wooriverygood/api/comment/repository/CommentLikeRepository.java
index 28c33f5..00e475d 100644
--- a/src/main/java/org/wooriverygood/api/comment/repository/CommentLikeRepository.java
+++ b/src/main/java/org/wooriverygood/api/comment/repository/CommentLikeRepository.java
@@ -3,14 +3,15 @@
import org.springframework.data.jpa.repository.JpaRepository;
import org.wooriverygood.api.comment.domain.Comment;
import org.wooriverygood.api.comment.domain.CommentLike;
+import org.wooriverygood.api.member.domain.Member;
import java.util.Optional;
public interface CommentLikeRepository extends JpaRepository {
- Optional findByCommentAndUsername(Comment comment, String username);
+ Optional findByCommentAndMember(Comment comment, Member member);
- boolean existsByCommentAndUsername(Comment comment, String username);
+ boolean existsByCommentAndMember(Comment comment, Member member);
void deleteAllByComment(Comment comment);
}
diff --git a/src/main/java/org/wooriverygood/api/comment/service/CommentService.java b/src/main/java/org/wooriverygood/api/comment/service/CommentService.java
deleted file mode 100644
index 7848b25..0000000
--- a/src/main/java/org/wooriverygood/api/comment/service/CommentService.java
+++ /dev/null
@@ -1,190 +0,0 @@
-package org.wooriverygood.api.comment.service;
-
-import org.springframework.stereotype.Service;
-import org.springframework.transaction.annotation.Transactional;
-import org.wooriverygood.api.advice.exception.ReplyDepthException;
-import org.wooriverygood.api.comment.domain.Comment;
-import org.wooriverygood.api.comment.domain.CommentLike;
-import org.wooriverygood.api.comment.dto.*;
-import org.wooriverygood.api.comment.repository.CommentLikeRepository;
-import org.wooriverygood.api.comment.repository.CommentRepository;
-import org.wooriverygood.api.advice.exception.CommentNotFoundException;
-import org.wooriverygood.api.advice.exception.PostNotFoundException;
-import org.wooriverygood.api.post.domain.Post;
-import org.wooriverygood.api.post.repository.PostRepository;
-import org.wooriverygood.api.support.AuthInfo;
-
-import java.util.List;
-import java.util.Objects;
-import java.util.Optional;
-
-@Service
-@Transactional(readOnly = true)
-public class CommentService {
-
- private final CommentRepository commentRepository;
- private final PostRepository postRepository;
- private final CommentLikeRepository commentLikeRepository;
-
-
- public CommentService(CommentRepository commentRepository, PostRepository postRepository, CommentLikeRepository commentLikeRepository) {
- this.commentRepository = commentRepository;
- this.postRepository = postRepository;
- this.commentLikeRepository = commentLikeRepository;
- }
-
- public List findAllComments(Long postId, AuthInfo authInfo) {
- List comments = commentRepository.findAllByPostId(postId);
- return comments.stream().map(comment -> convertToCommentResponse(comment, authInfo))
- .filter(response -> !Objects.isNull(response))
- .toList();
- }
-
- private CommentResponse convertToCommentResponse(Comment comment, AuthInfo authInfo) {
- if (comment.isReply())
- return null;
- if (comment.isSoftRemoved())
- return CommentResponse.softRemovedFrom(comment, convertToReplyResponses(comment, authInfo));
-
- boolean liked = commentLikeRepository.existsByCommentAndUsername(comment, authInfo.getUsername());
- return CommentResponse.from(comment, convertToReplyResponses(comment, authInfo), liked);
- }
-
- private List convertToReplyResponses(Comment parent, AuthInfo authInfo) {
- return parent.getChildren().stream().map(reply -> {
- boolean liked = commentLikeRepository.existsByCommentAndUsername(reply, authInfo.getUsername());
- return ReplyResponse.from(reply, liked);
- }).toList();
- }
-
- @Transactional
- public NewCommentResponse addComment(AuthInfo authInfo, Long postId, NewCommentRequest newCommentRequest) {
- Post post = postRepository.findById(postId)
- .orElseThrow(PostNotFoundException::new);
-
- Comment comment = Comment.builder()
- .content(newCommentRequest.getContent())
- .author(authInfo.getUsername())
- .post(post)
- .build();
- Comment saved = commentRepository.save(comment);
-
- return NewCommentResponse.builder()
- .comment_id(saved.getId())
- .content(saved.getContent())
- .author(saved.getAuthor())
- .build();
- }
-
- @Transactional
- public CommentLikeResponse likeComment(Long commentId, AuthInfo authInfo) {
- Comment comment = commentRepository.findById(commentId)
- .orElseThrow(CommentNotFoundException::new);
-
- Optional commentLike = commentLikeRepository.findByCommentAndUsername(comment, authInfo.getUsername());
-
- if (commentLike.isEmpty()) {
- addCommentLike(comment, authInfo.getUsername());
- return createCommentLikeResponse(comment, true);
- }
-
- deleteCommentLike(comment, commentLike.get());
- return createCommentLikeResponse(comment, false);
- }
-
- private void addCommentLike(Comment comment, String username) {
- CommentLike commentLike = CommentLike.builder()
- .comment(comment)
- .username(username)
- .build();
-
- comment.addCommentLike(commentLike);
- commentLikeRepository.save(commentLike);
- commentRepository.increaseLikeCount(comment.getId());
- }
-
- private void deleteCommentLike(Comment comment, CommentLike commentLike) {
- comment.deleteCommentLike(commentLike);
- commentRepository.decreaseLikeCount(comment.getId());
- }
-
- private CommentLikeResponse createCommentLikeResponse(Comment comment, boolean liked) {
- int likeCount = comment.getLikeCount() + (liked ? 1 : -1);
- return CommentLikeResponse.builder()
- .like_count(likeCount)
- .liked(liked)
- .build();
- }
-
- @Transactional
- public CommentUpdateResponse updateComment(Long commentId, CommentUpdateRequest request, AuthInfo authInfo) {
- Comment comment = commentRepository.findById(commentId)
- .orElseThrow(CommentNotFoundException::new);
- comment.validateAuthor(authInfo.getUsername());
-
- comment.updateContent(request.getContent());
-
- return CommentUpdateResponse.builder()
- .comment_id(comment.getId())
- .build();
- }
-
- @Transactional
- public CommentDeleteResponse deleteComment(Long commentId, AuthInfo authInfo) {
- Comment comment = commentRepository.findById(commentId)
- .orElseThrow(CommentNotFoundException::new);
-
- comment.validateAuthor(authInfo.getUsername());
-
- commentLikeRepository.deleteAllByComment(comment);
- deleteCommentOrReply(comment);
-
- return CommentDeleteResponse.builder()
- .comment_id(commentId)
- .build();
- }
-
- private void deleteCommentOrReply(Comment comment) {
- if (comment.isParent()) {
- deleteParent(comment);
- return;
- }
- deleteChild(comment);
- }
-
- private void deleteParent(Comment parent) {
- if (parent.hasNoReply()) {
- commentRepository.delete(parent);
- return;
- }
- parent.willBeDeleted();
- }
-
- private void deleteChild(Comment reply) {
- Comment parent = reply.getParent();
- parent.deleteChild(reply);
- commentRepository.delete(reply);
-
- if (parent.canDelete())
- commentRepository.delete(parent);
- }
-
- @Transactional
- public void addReply(Long commentId, NewReplyRequest request, AuthInfo authInfo) {
- Comment parent = commentRepository.findById(commentId)
- .orElseThrow(CommentNotFoundException::new);
-
- if (!parent.isParent())
- throw new ReplyDepthException();
-
- Comment child = Comment.builder()
- .content(request.getContent())
- .author(authInfo.getUsername())
- .post(parent.getPost())
- .parent(parent)
- .build();
- parent.addChildren(child);
-
- commentRepository.save(child);
- }
-}
diff --git a/src/main/java/org/wooriverygood/api/course/api/CourseApi.java b/src/main/java/org/wooriverygood/api/course/api/CourseApi.java
new file mode 100644
index 0000000..f69b9d2
--- /dev/null
+++ b/src/main/java/org/wooriverygood/api/course/api/CourseApi.java
@@ -0,0 +1,40 @@
+package org.wooriverygood.api.course.api;
+
+import jakarta.validation.Valid;
+import lombok.RequiredArgsConstructor;
+import org.springframework.http.HttpStatus;
+import org.springframework.http.ResponseEntity;
+import org.springframework.web.bind.annotation.*;
+import org.wooriverygood.api.course.application.CourseFindService;
+import org.wooriverygood.api.course.dto.*;
+import org.wooriverygood.api.course.application.CourseCreateService;
+
+@RestController
+@RequiredArgsConstructor
+@RequestMapping("/courses")
+public class CourseApi {
+
+ private final CourseCreateService courseCreateService;
+
+ private final CourseFindService courseFindService;
+
+
+ @GetMapping
+ public ResponseEntity findAllCourses() {
+ CoursesResponse response = courseFindService.findAll();
+ return ResponseEntity.ok(response);
+ }
+
+ @PostMapping
+ public ResponseEntity addCourse(@Valid @RequestBody NewCourseRequest newCourseRequest) {
+ courseCreateService.addCourse(newCourseRequest);
+ return ResponseEntity.status(HttpStatus.CREATED).build();
+ }
+
+ @GetMapping("/{id}/name")
+ public ResponseEntity findCourseName(@PathVariable("id") Long courseId) {
+ CourseNameResponse response = courseFindService.findCourseName(courseId);
+ return ResponseEntity.ok(response);
+ }
+
+}
diff --git a/src/main/java/org/wooriverygood/api/course/application/CourseCreateService.java b/src/main/java/org/wooriverygood/api/course/application/CourseCreateService.java
new file mode 100644
index 0000000..98dbd0a
--- /dev/null
+++ b/src/main/java/org/wooriverygood/api/course/application/CourseCreateService.java
@@ -0,0 +1,34 @@
+package org.wooriverygood.api.course.application;
+
+import lombok.RequiredArgsConstructor;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+import org.wooriverygood.api.course.domain.Course;
+import org.wooriverygood.api.course.dto.NewCourseRequest;
+import org.wooriverygood.api.course.repository.CourseRepository;
+
+@Service
+@Transactional(readOnly = true)
+@RequiredArgsConstructor
+public class CourseCreateService {
+
+ private final CourseRepository courseRepository;
+
+
+ @Transactional
+ public void addCourse(NewCourseRequest request) {
+ Course course = createCourse(request);
+ courseRepository.save(course);
+ }
+
+ private Course createCourse(NewCourseRequest request) {
+ return Course.builder()
+ .name(request.getCourseName())
+ .category(request.getCourseCategory())
+ .credit(request.getCourseCredit())
+ .isYouguan(request.getIsYouguan())
+ .kaikeYuanxi(request.getKaikeYuanxi())
+ .build();
+ }
+
+}
diff --git a/src/main/java/org/wooriverygood/api/course/application/CourseFindService.java b/src/main/java/org/wooriverygood/api/course/application/CourseFindService.java
new file mode 100644
index 0000000..b9875ae
--- /dev/null
+++ b/src/main/java/org/wooriverygood/api/course/application/CourseFindService.java
@@ -0,0 +1,38 @@
+package org.wooriverygood.api.course.application;
+
+import lombok.RequiredArgsConstructor;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+import org.wooriverygood.api.course.exception.CourseNotFoundException;
+import org.wooriverygood.api.course.domain.Course;
+import org.wooriverygood.api.course.dto.CourseNameResponse;
+import org.wooriverygood.api.course.dto.CourseResponse;
+import org.wooriverygood.api.course.dto.CoursesResponse;
+import org.wooriverygood.api.course.repository.CourseRepository;
+
+import java.util.List;
+
+@Service
+@Transactional(readOnly = true)
+@RequiredArgsConstructor
+public class CourseFindService {
+
+ private final CourseRepository courseRepository;
+
+ public CoursesResponse findAll() {
+ List responses = courseRepository.findAll()
+ .stream()
+ .map(CourseResponse::of)
+ .toList();
+
+ return new CoursesResponse(responses);
+ }
+
+ public CourseNameResponse findCourseName(Long courseId) {
+ Course course = courseRepository.findById(courseId)
+ .orElseThrow(CourseNotFoundException::new);
+
+ return new CourseNameResponse(course.getName());
+ }
+
+}
diff --git a/src/main/java/org/wooriverygood/api/course/controller/CourseController.java b/src/main/java/org/wooriverygood/api/course/controller/CourseController.java
deleted file mode 100644
index 01f2baa..0000000
--- a/src/main/java/org/wooriverygood/api/course/controller/CourseController.java
+++ /dev/null
@@ -1,39 +0,0 @@
-package org.wooriverygood.api.course.controller;
-
-import jakarta.validation.Valid;
-import lombok.RequiredArgsConstructor;
-import org.springframework.http.HttpStatus;
-import org.springframework.http.ResponseEntity;
-import org.springframework.web.bind.annotation.*;
-import org.wooriverygood.api.course.dto.CourseNameResponse;
-import org.wooriverygood.api.course.dto.CourseResponse;
-import org.wooriverygood.api.course.dto.NewCourseRequest;
-import org.wooriverygood.api.course.dto.NewCourseResponse;
-import org.wooriverygood.api.course.service.CourseService;
-
-import java.util.List;
-
-@RestController
-@RequiredArgsConstructor
-@RequestMapping("/courses")
-public class CourseController {
- private final CourseService courseService;
-
- @GetMapping
- public ResponseEntity> findAllCourses() {
- Listresponses = courseService.findAll();
- return ResponseEntity.ok(responses);
- }
-
- @PostMapping
- public ResponseEntity addCourse(@Valid @RequestBody NewCourseRequest newCourseRequest) {
- NewCourseResponse response = courseService.addCourse(newCourseRequest);
- return ResponseEntity.status(HttpStatus.CREATED).body(response);
- }
-
- @GetMapping("/{id}/name")
- public ResponseEntity getCourseName(@PathVariable("id") Long courseId) {
- CourseNameResponse response = courseService.getCourseName(courseId);
- return ResponseEntity.ok(response);
- }
-}
diff --git a/src/main/java/org/wooriverygood/api/course/domain/Courses.java b/src/main/java/org/wooriverygood/api/course/domain/Course.java
similarity index 69%
rename from src/main/java/org/wooriverygood/api/course/domain/Courses.java
rename to src/main/java/org/wooriverygood/api/course/domain/Course.java
index 77d1f31..bda5b32 100644
--- a/src/main/java/org/wooriverygood/api/course/domain/Courses.java
+++ b/src/main/java/org/wooriverygood/api/course/domain/Course.java
@@ -1,6 +1,7 @@
package org.wooriverygood.api.course.domain;
import jakarta.persistence.*;
+import lombok.AccessLevel;
import lombok.Builder;
import lombok.Getter;
import lombok.NoArgsConstructor;
@@ -8,22 +9,23 @@
@Entity
@Getter
-@NoArgsConstructor
@Table(name = "courses")
-public class Courses {
+@NoArgsConstructor(access = AccessLevel.PROTECTED)
+public class Course {
+
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "course_id")
private Long id;
@Column(name = "course_category", nullable = false)
- private String course_category;
+ private String category;
@Column(name = "course_credit", nullable = false)
- private double course_credit;
+ private double credit;
@Column(name = "course_name", nullable = false)
- private String course_name;
+ private String name;
@Column(name = "isYouguan", nullable = false)
private int isYouguan;
@@ -35,17 +37,16 @@ public class Courses {
@ColumnDefault("0")
private int reviewCount;
+
@Builder
- public Courses(Long id, String course_name, String course_category, double course_credit, int isYouguan, String kaikeYuanxi, int reviewCount) {
+ public Course(Long id, String name, String category, double credit, int isYouguan, String kaikeYuanxi, int reviewCount) {
this.id = id;
- this.course_name = course_name;
- this.course_category = course_category;
- this.course_credit = course_credit;
+ this.name = name;
+ this.category = category;
+ this.credit = credit;
this.isYouguan = isYouguan;
this.kaikeYuanxi = kaikeYuanxi;
this.reviewCount = reviewCount;
}
-
-
}
diff --git a/src/main/java/org/wooriverygood/api/course/dto/CourseNameResponse.java b/src/main/java/org/wooriverygood/api/course/dto/CourseNameResponse.java
index 80d1001..efb808d 100644
--- a/src/main/java/org/wooriverygood/api/course/dto/CourseNameResponse.java
+++ b/src/main/java/org/wooriverygood/api/course/dto/CourseNameResponse.java
@@ -1,14 +1,17 @@
package org.wooriverygood.api.course.dto;
-import lombok.Builder;
+import com.fasterxml.jackson.databind.PropertyNamingStrategies;
+import com.fasterxml.jackson.databind.annotation.JsonNaming;
import lombok.Getter;
@Getter
+@JsonNaming(PropertyNamingStrategies.SnakeCaseStrategy.class)
public class CourseNameResponse {
- private final String course_name;
- @Builder
- public CourseNameResponse(String course_name) {
- this.course_name = course_name;
+ private final String courseName;
+
+ public CourseNameResponse(String courseName) {
+ this.courseName = courseName;
}
+
}
diff --git a/src/main/java/org/wooriverygood/api/course/dto/CourseResponse.java b/src/main/java/org/wooriverygood/api/course/dto/CourseResponse.java
index 1281328..cec3cc6 100644
--- a/src/main/java/org/wooriverygood/api/course/dto/CourseResponse.java
+++ b/src/main/java/org/wooriverygood/api/course/dto/CourseResponse.java
@@ -1,39 +1,51 @@
package org.wooriverygood.api.course.dto;
+import com.fasterxml.jackson.databind.PropertyNamingStrategies;
+import com.fasterxml.jackson.databind.annotation.JsonNaming;
import lombok.Builder;
import lombok.Getter;
-import org.wooriverygood.api.course.domain.Courses;
+import org.wooriverygood.api.course.domain.Course;
@Getter
+@JsonNaming(PropertyNamingStrategies.SnakeCaseStrategy.class)
public class CourseResponse {
- private final Long course_id;
- private final String course_category;
- private final double course_credit;
- private final String course_name;
+
+ private final Long courseId;
+
+ private final String courseCategory;
+
+ private final double courseCredit;
+
+ private final String courseName;
+
private final int isYouguan;
+
private final String kaikeYuanxi;
+
private final int reviewCount;
+
@Builder
- public CourseResponse(Long course_id, String course_category, double course_credit, String course_name, int isYouguan, String kaikeYuanxi, int reviewCount) {
- this.course_id = course_id;
- this.course_category = course_category;
- this.course_credit = course_credit;
- this.course_name = course_name;
+ public CourseResponse(Long courseId, String courseCategory, double courseCredit, String courseName, int isYouguan, String kaikeYuanxi, int reviewCount) {
+ this.courseId = courseId;
+ this.courseCategory = courseCategory;
+ this.courseCredit = courseCredit;
+ this.courseName = courseName;
this.isYouguan = isYouguan;
this.kaikeYuanxi = kaikeYuanxi;
this.reviewCount = reviewCount;
}
- public static CourseResponse from(Courses course) {
+ public static CourseResponse of(Course course) {
return CourseResponse.builder()
- .course_id(course.getId())
- .course_category(course.getCourse_category())
- .course_credit(course.getCourse_credit())
- .course_name(course.getCourse_name())
+ .courseId(course.getId())
+ .courseCategory(course.getCategory())
+ .courseCredit(course.getCredit())
+ .courseName(course.getName())
.isYouguan(course.getIsYouguan())
.kaikeYuanxi(course.getKaikeYuanxi())
.reviewCount(course.getReviewCount())
.build();
}
+
}
diff --git a/src/main/java/org/wooriverygood/api/course/dto/CoursesResponse.java b/src/main/java/org/wooriverygood/api/course/dto/CoursesResponse.java
new file mode 100644
index 0000000..c1164be
--- /dev/null
+++ b/src/main/java/org/wooriverygood/api/course/dto/CoursesResponse.java
@@ -0,0 +1,17 @@
+package org.wooriverygood.api.course.dto;
+
+import lombok.Getter;
+
+import java.util.List;
+
+@Getter
+public class CoursesResponse {
+
+ private final List courses;
+
+
+ public CoursesResponse(List courses) {
+ this.courses = courses;
+ }
+
+}
diff --git a/src/main/java/org/wooriverygood/api/course/dto/NewCourseRequest.java b/src/main/java/org/wooriverygood/api/course/dto/NewCourseRequest.java
index 9706cf3..e86dbae 100644
--- a/src/main/java/org/wooriverygood/api/course/dto/NewCourseRequest.java
+++ b/src/main/java/org/wooriverygood/api/course/dto/NewCourseRequest.java
@@ -1,22 +1,32 @@
package org.wooriverygood.api.course.dto;
+import com.fasterxml.jackson.databind.PropertyNamingStrategies;
+import com.fasterxml.jackson.databind.annotation.JsonNaming;
import lombok.Builder;
import lombok.Getter;
@Getter
+@JsonNaming(PropertyNamingStrategies.SnakeCaseStrategy.class)
public class NewCourseRequest {
- private final String course_name;
- private final double course_credit;
- private final String course_category;
+
+ private final String courseName;
+
+ private final double courseCredit;
+
+ private final String courseCategory;
+
private final String kaikeYuanxi;
+
private final int isYouguan;
+
@Builder
- public NewCourseRequest(String course_name, double course_credit, String course_category, String kaikeYuanxi, int isYouguan) {
- this.course_name = course_name;
- this.course_credit = course_credit;
- this.course_category = course_category;
+ public NewCourseRequest(String courseName, double courseCredit, String courseCategory, String kaikeYuanxi, int isYouguan) {
+ this.courseName = courseName;
+ this.courseCredit = courseCredit;
+ this.courseCategory = courseCategory;
this.kaikeYuanxi = kaikeYuanxi;
this.isYouguan = isYouguan;
}
+
}
diff --git a/src/main/java/org/wooriverygood/api/course/dto/NewCourseResponse.java b/src/main/java/org/wooriverygood/api/course/dto/NewCourseResponse.java
deleted file mode 100644
index 5671bcb..0000000
--- a/src/main/java/org/wooriverygood/api/course/dto/NewCourseResponse.java
+++ /dev/null
@@ -1,24 +0,0 @@
-package org.wooriverygood.api.course.dto;
-
-import lombok.Builder;
-import lombok.Getter;
-
-@Getter
-public class NewCourseResponse {
- private final Long course_id;
- private final String course_name;
- private final double course_credit;
- private final String course_category;
- private final String kaikeYuanxi;
- private final int isYouguan;
-
- @Builder
- public NewCourseResponse(Long course_id, String course_name, double course_credit, String course_category, String kaikeYuanxi, int isYouguan) {
- this.course_id = course_id;
- this.course_name = course_name;
- this.course_credit = course_credit;
- this.course_category = course_category;
- this.kaikeYuanxi = kaikeYuanxi;
- this.isYouguan = isYouguan;
- }
-}
diff --git a/src/main/java/org/wooriverygood/api/advice/exception/CourseNotFoundException.java b/src/main/java/org/wooriverygood/api/course/exception/CourseNotFoundException.java
similarity index 63%
rename from src/main/java/org/wooriverygood/api/advice/exception/CourseNotFoundException.java
rename to src/main/java/org/wooriverygood/api/course/exception/CourseNotFoundException.java
index a594c6f..9ef3403 100644
--- a/src/main/java/org/wooriverygood/api/advice/exception/CourseNotFoundException.java
+++ b/src/main/java/org/wooriverygood/api/course/exception/CourseNotFoundException.java
@@ -1,6 +1,6 @@
-package org.wooriverygood.api.advice.exception;
+package org.wooriverygood.api.course.exception;
-import org.wooriverygood.api.advice.exception.general.NotFoundException;
+import org.wooriverygood.api.global.error.exception.NotFoundException;
public class CourseNotFoundException extends NotFoundException {
private static final String MESSAGE = "강의를 찾을 수 없습니다.";
diff --git a/src/main/java/org/wooriverygood/api/course/repository/CourseRepository.java b/src/main/java/org/wooriverygood/api/course/repository/CourseRepository.java
index 29d009e..5853702 100644
--- a/src/main/java/org/wooriverygood/api/course/repository/CourseRepository.java
+++ b/src/main/java/org/wooriverygood/api/course/repository/CourseRepository.java
@@ -1,7 +1,21 @@
package org.wooriverygood.api.course.repository;
import org.springframework.data.jpa.repository.JpaRepository;
-import org.wooriverygood.api.course.domain.Courses;
+import org.springframework.data.jpa.repository.Modifying;
+import org.springframework.data.jpa.repository.Query;
+import org.springframework.transaction.annotation.Transactional;
+import org.wooriverygood.api.course.domain.Course;
+
+public interface CourseRepository extends JpaRepository {
+
+ @Transactional
+ @Modifying(clearAutomatically = true)
+ @Query(value = "UPDATE courses SET review_count = review_count + 1 WHERE course_id = :courseId", nativeQuery = true)
+ void increaseReviewCount(Long courseId);
+
+ @Transactional
+ @Modifying(clearAutomatically = true)
+ @Query(value = "UPDATE courses SET review_count = review_count - 1 WHERE course_id = :courseId", nativeQuery = true)
+ void decreaseReviewCount(Long courseId);
-public interface CourseRepository extends JpaRepository {
}
diff --git a/src/main/java/org/wooriverygood/api/course/service/CourseService.java b/src/main/java/org/wooriverygood/api/course/service/CourseService.java
deleted file mode 100644
index 74341ec..0000000
--- a/src/main/java/org/wooriverygood/api/course/service/CourseService.java
+++ /dev/null
@@ -1,64 +0,0 @@
-package org.wooriverygood.api.course.service;
-
-import lombok.RequiredArgsConstructor;
-import org.springframework.stereotype.Service;
-import org.springframework.transaction.annotation.Transactional;
-import org.wooriverygood.api.advice.exception.CourseNotFoundException;
-import org.wooriverygood.api.course.domain.Courses;
-import org.wooriverygood.api.course.dto.CourseNameResponse;
-import org.wooriverygood.api.course.dto.CourseResponse;
-import org.wooriverygood.api.course.dto.NewCourseRequest;
-import org.wooriverygood.api.course.dto.NewCourseResponse;
-import org.wooriverygood.api.course.repository.CourseRepository;
-
-import java.util.List;
-
-@RequiredArgsConstructor
-@Service
-@Transactional(readOnly = true)
-public class CourseService {
- private final CourseRepository courseRepository;
-
- public List findAll() {
- return courseRepository.findAll().stream().map(CourseResponse::from).toList();
- }
-
- @Transactional
- public NewCourseResponse addCourse(NewCourseRequest newCourseRequest) {
- Courses course = createCourse(newCourseRequest);
- Courses saved = courseRepository.save(course);
- return createResponse(saved);
- }
-
- public CourseNameResponse getCourseName(Long courseId) {
- Courses courses = courseRepository.findById(courseId)
- .orElseThrow(CourseNotFoundException::new);
-
- return CourseNameResponse.builder()
- .course_name(courses.getCourse_name())
- .build();
-
- }
-
- private Courses createCourse(NewCourseRequest newCourseRequest) {
- return Courses.builder()
- .course_name(newCourseRequest.getCourse_name())
- .course_category(newCourseRequest.getCourse_category())
- .course_credit(newCourseRequest.getCourse_credit())
- .isYouguan(newCourseRequest.getIsYouguan())
- .kaikeYuanxi(newCourseRequest.getKaikeYuanxi())
- .build();
- }
-
- private NewCourseResponse createResponse(Courses course) {
- return NewCourseResponse.builder()
- .course_id(course.getId())
- .course_name(course.getCourse_name())
- .course_category(course.getCourse_category())
- .course_credit(course.getCourse_credit())
- .isYouguan(course.getIsYouguan())
- .kaikeYuanxi(course.getKaikeYuanxi())
- .build();
- }
-
-}
diff --git a/src/main/java/org/wooriverygood/api/global/ErrorResponse.java b/src/main/java/org/wooriverygood/api/global/ErrorResponse.java
new file mode 100644
index 0000000..b469f51
--- /dev/null
+++ b/src/main/java/org/wooriverygood/api/global/ErrorResponse.java
@@ -0,0 +1,44 @@
+package org.wooriverygood.api.global;
+
+import lombok.AccessLevel;
+import lombok.Builder;
+import lombok.Getter;
+import lombok.NoArgsConstructor;
+
+import java.util.List;
+
+@Getter
+@NoArgsConstructor(access = AccessLevel.PROTECTED)
+public class ErrorResponse {
+
+ private String message;
+
+ private List errors;
+
+ @Builder
+ public ErrorResponse(String message, List errors) {
+ this.message = message;
+ this.errors = errors;
+ }
+
+ @Getter
+ @NoArgsConstructor(access = AccessLevel.PROTECTED)
+ public static class FieldError {
+
+ private String field;
+
+ private String value;
+
+ private String reason;
+
+
+ @Builder
+ public FieldError(String field, String value, String reason) {
+ this.field = field;
+ this.value = value;
+ this.reason = reason;
+ }
+
+ }
+
+}
diff --git a/src/main/java/org/wooriverygood/api/support/AuthInfo.java b/src/main/java/org/wooriverygood/api/global/auth/AuthInfo.java
similarity index 56%
rename from src/main/java/org/wooriverygood/api/support/AuthInfo.java
rename to src/main/java/org/wooriverygood/api/global/auth/AuthInfo.java
index ac5aa9d..4aa4f01 100644
--- a/src/main/java/org/wooriverygood/api/support/AuthInfo.java
+++ b/src/main/java/org/wooriverygood/api/global/auth/AuthInfo.java
@@ -1,4 +1,4 @@
-package org.wooriverygood.api.support;
+package org.wooriverygood.api.global.auth;
import lombok.Builder;
import lombok.Getter;
@@ -6,11 +6,15 @@
@Getter
public class AuthInfo {
+ private Long memberId;
+
private final String sub;
+
private final String username;
@Builder
- public AuthInfo(String sub, String username) {
+ public AuthInfo(Long memberId, String sub, String username) {
+ this.memberId = memberId;
this.sub = sub;
this.username = username;
}
diff --git a/src/main/java/org/wooriverygood/api/support/AuthenticationPrincipalArgumentResolver.java b/src/main/java/org/wooriverygood/api/global/auth/AuthenticationPrincipalArgumentResolver.java
similarity index 57%
rename from src/main/java/org/wooriverygood/api/support/AuthenticationPrincipalArgumentResolver.java
rename to src/main/java/org/wooriverygood/api/global/auth/AuthenticationPrincipalArgumentResolver.java
index bfb1408..c0d17da 100644
--- a/src/main/java/org/wooriverygood/api/support/AuthenticationPrincipalArgumentResolver.java
+++ b/src/main/java/org/wooriverygood/api/global/auth/AuthenticationPrincipalArgumentResolver.java
@@ -1,5 +1,6 @@
-package org.wooriverygood.api.support;
+package org.wooriverygood.api.global.auth;
+import lombok.RequiredArgsConstructor;
import org.springframework.core.MethodParameter;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
@@ -8,19 +9,31 @@
import org.springframework.web.context.request.NativeWebRequest;
import org.springframework.web.method.support.HandlerMethodArgumentResolver;
import org.springframework.web.method.support.ModelAndViewContainer;
+import org.wooriverygood.api.member.domain.Member;
+import org.wooriverygood.api.member.repository.MemberRepository;
+@RequiredArgsConstructor
public class AuthenticationPrincipalArgumentResolver implements HandlerMethodArgumentResolver {
+ private final MemberRepository memberRepository;
+
+
@Override
public boolean supportsParameter(MethodParameter parameter) {
return parameter.hasParameterAnnotation(Login.class);
}
@Override
- public Object resolveArgument(MethodParameter parameter, ModelAndViewContainer mavContainer, NativeWebRequest webRequest, WebDataBinderFactory binderFactory) {
+ public Object resolveArgument(MethodParameter parameter,
+ ModelAndViewContainer mavContainer,
+ NativeWebRequest webRequest,
+ WebDataBinderFactory binderFactory) {
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
- if (authentication != null && authentication.getPrincipal() instanceof Jwt jwt) return createAuthInfo(jwt);
+ if (authentication != null && authentication.getPrincipal() instanceof Jwt jwt) {
+ return createAuthInfo(jwt);
+ }
+
return AuthInfo.builder().build();
}
@@ -28,7 +41,14 @@ private AuthInfo createAuthInfo(Jwt jwt) {
String sub = jwt.getClaim("sub");
String username = jwt.getClaim("username");
+ Member member = memberRepository.findByUsername(username)
+ .orElseGet(() -> {
+ Member newMember = Member.builder().username(username).build();
+ return memberRepository.save(newMember);
+ });
+
return AuthInfo.builder()
+ .memberId(member.getId())
.sub(sub)
.username(username)
.build();
diff --git a/src/main/java/org/wooriverygood/api/support/Login.java b/src/main/java/org/wooriverygood/api/global/auth/Login.java
similarity index 85%
rename from src/main/java/org/wooriverygood/api/support/Login.java
rename to src/main/java/org/wooriverygood/api/global/auth/Login.java
index c577a3c..6560507 100644
--- a/src/main/java/org/wooriverygood/api/support/Login.java
+++ b/src/main/java/org/wooriverygood/api/global/auth/Login.java
@@ -1,4 +1,4 @@
-package org.wooriverygood.api.support;
+package org.wooriverygood.api.global.auth;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
diff --git a/src/main/java/org/wooriverygood/api/support/TokenProvider.java b/src/main/java/org/wooriverygood/api/global/auth/TokenProvider.java
similarity index 91%
rename from src/main/java/org/wooriverygood/api/support/TokenProvider.java
rename to src/main/java/org/wooriverygood/api/global/auth/TokenProvider.java
index 1121370..afcbba6 100644
--- a/src/main/java/org/wooriverygood/api/support/TokenProvider.java
+++ b/src/main/java/org/wooriverygood/api/global/auth/TokenProvider.java
@@ -1,4 +1,4 @@
-package org.wooriverygood.api.support;
+package org.wooriverygood.api.global.auth;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.security.oauth2.jwt.JwtDecoder;
diff --git a/src/main/java/org/wooriverygood/api/config/JpaConfig.java b/src/main/java/org/wooriverygood/api/global/config/JpaConfig.java
similarity index 81%
rename from src/main/java/org/wooriverygood/api/config/JpaConfig.java
rename to src/main/java/org/wooriverygood/api/global/config/JpaConfig.java
index c451e9d..68da2a6 100644
--- a/src/main/java/org/wooriverygood/api/config/JpaConfig.java
+++ b/src/main/java/org/wooriverygood/api/global/config/JpaConfig.java
@@ -1,4 +1,4 @@
-package org.wooriverygood.api.config;
+package org.wooriverygood.api.global.config;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.jpa.repository.config.EnableJpaAuditing;
diff --git a/src/main/java/org/wooriverygood/api/config/SecurityConfig.java b/src/main/java/org/wooriverygood/api/global/config/SecurityConfig.java
similarity index 88%
rename from src/main/java/org/wooriverygood/api/config/SecurityConfig.java
rename to src/main/java/org/wooriverygood/api/global/config/SecurityConfig.java
index 7157027..c1118a4 100644
--- a/src/main/java/org/wooriverygood/api/config/SecurityConfig.java
+++ b/src/main/java/org/wooriverygood/api/global/config/SecurityConfig.java
@@ -1,4 +1,4 @@
-package org.wooriverygood.api.config;
+package org.wooriverygood.api.global.config;
import lombok.RequiredArgsConstructor;
import org.springframework.context.annotation.Bean;
@@ -12,7 +12,7 @@
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configurers.AbstractHttpConfigurer;
import org.springframework.security.web.SecurityFilterChain;
-import org.wooriverygood.api.support.TokenProvider;
+import org.wooriverygood.api.global.auth.TokenProvider;
@Configuration
@EnableWebSecurity
@@ -36,7 +36,7 @@ public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Excepti
.authorizeHttpRequests(authorizationManagerRequestMatcherRegistry ->
authorizationManagerRequestMatcherRegistry
.requestMatchers(HttpMethod.OPTIONS).permitAll()
- .requestMatchers("/", "/docs/**", "/docs/index.html").permitAll() // 권한이 필요없는 엔드 포인트, 루트와 API 명세 엔드포인트 열어둠
+ .requestMatchers("/*", "/docs/**", "/docs/index.html").permitAll()
.anyRequest().authenticated())
.oauth2ResourceServer(httpSecurityOAuth2ResourceServerConfigurer ->
httpSecurityOAuth2ResourceServerConfigurer.jwt(jwtConfigurer ->
diff --git a/src/main/java/org/wooriverygood/api/config/WebConfig.java b/src/main/java/org/wooriverygood/api/global/config/WebConfig.java
similarity index 72%
rename from src/main/java/org/wooriverygood/api/config/WebConfig.java
rename to src/main/java/org/wooriverygood/api/global/config/WebConfig.java
index 0525104..9278e3c 100644
--- a/src/main/java/org/wooriverygood/api/config/WebConfig.java
+++ b/src/main/java/org/wooriverygood/api/global/config/WebConfig.java
@@ -1,20 +1,25 @@
-package org.wooriverygood.api.config;
+package org.wooriverygood.api.global.config;
+import lombok.RequiredArgsConstructor;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.method.support.HandlerMethodArgumentResolver;
import org.springframework.web.servlet.config.annotation.CorsRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
-import org.wooriverygood.api.support.AuthenticationPrincipalArgumentResolver;
+import org.wooriverygood.api.global.auth.AuthenticationPrincipalArgumentResolver;
+import org.wooriverygood.api.member.repository.MemberRepository;
import java.util.List;
@Configuration
+@RequiredArgsConstructor
public class WebConfig implements WebMvcConfigurer {
+ private final MemberRepository memberRepository;
+
@Bean
public AuthenticationPrincipalArgumentResolver authenticationPrincipalArgumentResolver() {
- return new AuthenticationPrincipalArgumentResolver();
+ return new AuthenticationPrincipalArgumentResolver(memberRepository);
}
@Override
diff --git a/src/main/java/org/wooriverygood/api/global/error/ControllerAdvice.java b/src/main/java/org/wooriverygood/api/global/error/ControllerAdvice.java
new file mode 100644
index 0000000..ef6b35c
--- /dev/null
+++ b/src/main/java/org/wooriverygood/api/global/error/ControllerAdvice.java
@@ -0,0 +1,63 @@
+package org.wooriverygood.api.global.error;
+
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.http.HttpStatus;
+import org.springframework.http.ResponseEntity;
+import org.springframework.validation.BindingResult;
+import org.springframework.validation.FieldError;
+import org.springframework.web.bind.MethodArgumentNotValidException;
+import org.springframework.web.bind.annotation.ExceptionHandler;
+import org.springframework.web.bind.annotation.RestControllerAdvice;
+import org.wooriverygood.api.global.ErrorResponse;
+import org.wooriverygood.api.global.error.exception.BadRequestException;
+import org.wooriverygood.api.global.error.exception.ForbiddenException;
+import org.wooriverygood.api.global.error.exception.NotFoundException;
+
+import java.util.ArrayList;
+import java.util.List;
+
+@Slf4j
+@RestControllerAdvice
+public class ControllerAdvice {
+
+ @ExceptionHandler(ForbiddenException.class)
+ public ResponseEntity handleForbiddenException(ForbiddenException e) {
+ return ResponseEntity.status(HttpStatus.FORBIDDEN)
+ .body(new ErrorResponse(e.getMessage(), new ArrayList<>()));
+ }
+
+ @ExceptionHandler(NotFoundException.class)
+ public ResponseEntity handleNotFoundException(NotFoundException e) {
+ return ResponseEntity.status(HttpStatus.NOT_FOUND)
+ .body(new ErrorResponse(e.getMessage(), new ArrayList<>()));
+ }
+
+ @ExceptionHandler(BadRequestException.class)
+ public ResponseEntity handleBadRequestException(BadRequestException e) {
+ return ResponseEntity.status(HttpStatus.BAD_REQUEST)
+ .body(new ErrorResponse(e.getMessage(), new ArrayList<>()));
+ }
+
+ @ExceptionHandler(MethodArgumentNotValidException.class)
+ public ResponseEntity handleMethodArgumentNotValidException(MethodArgumentNotValidException e) {
+ log.error(e.getMessage());
+ final BindingResult bindingResult = e.getBindingResult();
+ final List errors = bindingResult.getFieldErrors();
+ return ResponseEntity.status(HttpStatus.BAD_REQUEST)
+ .body(buildFieldErrors("Invalid input value", errors.stream()
+ .map(error -> ErrorResponse.FieldError.builder()
+ .reason(error.getDefaultMessage())
+ .field(error.getField())
+ .value((String) error.getRejectedValue())
+ .build())
+ .toList()));
+ }
+
+ private ErrorResponse buildFieldErrors(String message, List errors) {
+ return ErrorResponse.builder()
+ .message(message)
+ .errors(errors)
+ .build();
+ }
+
+}
diff --git a/src/main/java/org/wooriverygood/api/advice/exception/AuthorizationException.java b/src/main/java/org/wooriverygood/api/global/error/exception/AuthorizationException.java
similarity index 62%
rename from src/main/java/org/wooriverygood/api/advice/exception/AuthorizationException.java
rename to src/main/java/org/wooriverygood/api/global/error/exception/AuthorizationException.java
index e3dcb1c..4fa6e29 100644
--- a/src/main/java/org/wooriverygood/api/advice/exception/AuthorizationException.java
+++ b/src/main/java/org/wooriverygood/api/global/error/exception/AuthorizationException.java
@@ -1,6 +1,6 @@
-package org.wooriverygood.api.advice.exception;
+package org.wooriverygood.api.global.error.exception;
-import org.wooriverygood.api.advice.exception.general.ForbiddenException;
+import org.wooriverygood.api.global.error.exception.ForbiddenException;
public class AuthorizationException extends ForbiddenException {
diff --git a/src/main/java/org/wooriverygood/api/advice/exception/general/BadRequestException.java b/src/main/java/org/wooriverygood/api/global/error/exception/BadRequestException.java
similarity index 71%
rename from src/main/java/org/wooriverygood/api/advice/exception/general/BadRequestException.java
rename to src/main/java/org/wooriverygood/api/global/error/exception/BadRequestException.java
index cc56612..f9ac968 100644
--- a/src/main/java/org/wooriverygood/api/advice/exception/general/BadRequestException.java
+++ b/src/main/java/org/wooriverygood/api/global/error/exception/BadRequestException.java
@@ -1,4 +1,4 @@
-package org.wooriverygood.api.advice.exception.general;
+package org.wooriverygood.api.global.error.exception;
public class BadRequestException extends BusinessException {
diff --git a/src/main/java/org/wooriverygood/api/advice/exception/general/BusinessException.java b/src/main/java/org/wooriverygood/api/global/error/exception/BusinessException.java
similarity index 71%
rename from src/main/java/org/wooriverygood/api/advice/exception/general/BusinessException.java
rename to src/main/java/org/wooriverygood/api/global/error/exception/BusinessException.java
index 550a84a..5ed0b7a 100644
--- a/src/main/java/org/wooriverygood/api/advice/exception/general/BusinessException.java
+++ b/src/main/java/org/wooriverygood/api/global/error/exception/BusinessException.java
@@ -1,4 +1,4 @@
-package org.wooriverygood.api.advice.exception.general;
+package org.wooriverygood.api.global.error.exception;
public class BusinessException extends RuntimeException {
diff --git a/src/main/java/org/wooriverygood/api/advice/exception/general/ForbiddenException.java b/src/main/java/org/wooriverygood/api/global/error/exception/ForbiddenException.java
similarity index 71%
rename from src/main/java/org/wooriverygood/api/advice/exception/general/ForbiddenException.java
rename to src/main/java/org/wooriverygood/api/global/error/exception/ForbiddenException.java
index ca44c23..2dd36c7 100644
--- a/src/main/java/org/wooriverygood/api/advice/exception/general/ForbiddenException.java
+++ b/src/main/java/org/wooriverygood/api/global/error/exception/ForbiddenException.java
@@ -1,4 +1,4 @@
-package org.wooriverygood.api.advice.exception.general;
+package org.wooriverygood.api.global.error.exception;
public class ForbiddenException extends BusinessException {
diff --git a/src/main/java/org/wooriverygood/api/advice/exception/general/NotFoundException.java b/src/main/java/org/wooriverygood/api/global/error/exception/NotFoundException.java
similarity index 71%
rename from src/main/java/org/wooriverygood/api/advice/exception/general/NotFoundException.java
rename to src/main/java/org/wooriverygood/api/global/error/exception/NotFoundException.java
index 12ddff6..89547a5 100644
--- a/src/main/java/org/wooriverygood/api/advice/exception/general/NotFoundException.java
+++ b/src/main/java/org/wooriverygood/api/global/error/exception/NotFoundException.java
@@ -1,4 +1,4 @@
-package org.wooriverygood.api.advice.exception.general;
+package org.wooriverygood.api.global.error.exception;
public class NotFoundException extends BusinessException {
diff --git a/src/main/java/org/wooriverygood/api/member/domain/Member.java b/src/main/java/org/wooriverygood/api/member/domain/Member.java
new file mode 100644
index 0000000..6dbee95
--- /dev/null
+++ b/src/main/java/org/wooriverygood/api/member/domain/Member.java
@@ -0,0 +1,56 @@
+package org.wooriverygood.api.member.domain;
+
+import jakarta.persistence.*;
+import lombok.AccessLevel;
+import lombok.Builder;
+import lombok.Getter;
+import lombok.NoArgsConstructor;
+import org.wooriverygood.api.global.error.exception.AuthorizationException;
+import org.wooriverygood.api.review.domain.Review;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Objects;
+
+@Entity
+@Getter
+@Table(name = "members")
+@NoArgsConstructor(access = AccessLevel.PROTECTED)
+public class Member {
+
+ @Id
+ @GeneratedValue(strategy = GenerationType.IDENTITY)
+ private Long id;
+
+ private String username;
+
+ @Column(name = "old_username")
+ private String oldUsername;
+
+ private String email;
+
+ @OneToMany(mappedBy = "member")
+ private List reviews = new ArrayList<>();
+
+
+ @Builder
+ public Member(Long id, String username) {
+ this.id = id;
+ this.username = username;
+ }
+
+ public boolean isSame(Member member) {
+ return Objects.equals(this.id, member.getId());
+ }
+
+ public void verify(Member member) {
+ if (!isSame(member)) {
+ throw new AuthorizationException();
+ }
+ }
+
+ public void addReview(Review review) {
+ reviews.add(review);
+ }
+
+}
\ No newline at end of file
diff --git a/src/main/java/org/wooriverygood/api/member/exception/MemberNotFoundException.java b/src/main/java/org/wooriverygood/api/member/exception/MemberNotFoundException.java
new file mode 100644
index 0000000..7581fc4
--- /dev/null
+++ b/src/main/java/org/wooriverygood/api/member/exception/MemberNotFoundException.java
@@ -0,0 +1,12 @@
+package org.wooriverygood.api.member.exception;
+
+import org.wooriverygood.api.global.error.exception.NotFoundException;
+
+public class MemberNotFoundException extends NotFoundException {
+ private static final String MESSAGE = "유저 정보를 찾을 수 없습니다.";
+
+
+ public MemberNotFoundException() {
+ super(MESSAGE);
+ }
+}
diff --git a/src/main/java/org/wooriverygood/api/member/repository/MemberRepository.java b/src/main/java/org/wooriverygood/api/member/repository/MemberRepository.java
new file mode 100644
index 0000000..d10e1df
--- /dev/null
+++ b/src/main/java/org/wooriverygood/api/member/repository/MemberRepository.java
@@ -0,0 +1,12 @@
+package org.wooriverygood.api.member.repository;
+
+import org.springframework.data.jpa.repository.JpaRepository;
+import org.wooriverygood.api.member.domain.Member;
+
+import java.util.Optional;
+
+public interface MemberRepository extends JpaRepository {
+
+ Optional findByUsername(String username);
+
+}
diff --git a/src/main/java/org/wooriverygood/api/post/api/PostApi.java b/src/main/java/org/wooriverygood/api/post/api/PostApi.java
new file mode 100644
index 0000000..9bce494
--- /dev/null
+++ b/src/main/java/org/wooriverygood/api/post/api/PostApi.java
@@ -0,0 +1,87 @@
+package org.wooriverygood.api.post.api;
+
+import jakarta.validation.Valid;
+import lombok.RequiredArgsConstructor;
+import org.springframework.data.domain.PageRequest;
+import org.springframework.data.domain.Pageable;
+import org.springframework.http.HttpStatus;
+import org.springframework.http.ResponseEntity;
+import org.springframework.web.bind.annotation.*;
+import org.wooriverygood.api.post.application.*;
+import org.wooriverygood.api.post.dto.*;
+import org.wooriverygood.api.global.auth.AuthInfo;
+import org.wooriverygood.api.global.auth.Login;
+
+
+@RestController
+@RequestMapping("/posts")
+@RequiredArgsConstructor
+public class PostApi {
+
+ private final PostLikeToggleService postLikeToggleService;
+
+ private final PostFindService postFindService;
+
+ private final PostCreateService postCreateService;
+
+ private final PostDeleteService postDeleteService;
+
+ private final PostUpdateService postUpdateService;
+
+ private final int PAGE_SIZE = 10;
+
+
+ @GetMapping
+ public ResponseEntity findPosts(@Login AuthInfo authInfo,
+ @RequestParam(required = false, defaultValue = "0", value = "page") int pageNo,
+ @RequestParam(required = false, defaultValue = "", value = "category") String category) {
+ Pageable pageable = PageRequest.of(pageNo, PAGE_SIZE);
+ PostsResponse response = postFindService.findPosts(authInfo, pageable, category);
+ return ResponseEntity.ok(response);
+ }
+
+ @GetMapping("/{id}")
+ public ResponseEntity findPostById(@PathVariable("id") Long postId,
+ @Login AuthInfo authInfo) {
+ PostDetailResponse postDetailResponse = postFindService.findPostById(postId, authInfo);
+ return ResponseEntity.ok(postDetailResponse);
+ }
+
+ @PostMapping
+ public ResponseEntity addPost(@Login AuthInfo authInfo,
+ @Valid @RequestBody NewPostRequest newPostRequest) {
+ postCreateService.addPost(authInfo, newPostRequest);
+ return ResponseEntity.status(HttpStatus.CREATED).build();
+ }
+
+ @GetMapping("/me")
+ public ResponseEntity findMyPosts(@Login AuthInfo authInfo,
+ @RequestParam(required = false, defaultValue = "0", value = "page") int pageNo) {
+ Pageable pageable = PageRequest.of(pageNo, PAGE_SIZE);
+ PostsResponse response = postFindService.findMyPosts(authInfo, pageable);
+ return ResponseEntity.ok(response);
+ }
+
+ @PutMapping("/{id}/like")
+ public ResponseEntity togglePostLike(@PathVariable("id") Long postId,
+ @Login AuthInfo authInfo) {
+ PostLikeResponse response = postLikeToggleService.togglePostLike(postId, authInfo);
+ return ResponseEntity.ok(response);
+ }
+
+ @PutMapping("/{id}")
+ public ResponseEntity updatePost(@PathVariable("id") Long postId,
+ @Valid @RequestBody PostUpdateRequest postUpdateRequest,
+ @Login AuthInfo authInfo) {
+ postUpdateService.updatePost(postId, postUpdateRequest, authInfo);
+ return ResponseEntity.noContent().build();
+ }
+
+ @DeleteMapping("/{id}")
+ public ResponseEntity deletePost(@PathVariable("id") Long postId,
+ @Login AuthInfo authInfo) {
+ postDeleteService.deletePost(authInfo, postId);
+ return ResponseEntity.noContent().build();
+ }
+
+}
diff --git a/src/main/java/org/wooriverygood/api/post/application/PostCreateService.java b/src/main/java/org/wooriverygood/api/post/application/PostCreateService.java
new file mode 100644
index 0000000..403b65a
--- /dev/null
+++ b/src/main/java/org/wooriverygood/api/post/application/PostCreateService.java
@@ -0,0 +1,41 @@
+package org.wooriverygood.api.post.application;
+
+import lombok.RequiredArgsConstructor;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+import org.wooriverygood.api.member.domain.Member;
+import org.wooriverygood.api.member.exception.MemberNotFoundException;
+import org.wooriverygood.api.member.repository.MemberRepository;
+import org.wooriverygood.api.post.domain.Post;
+import org.wooriverygood.api.post.domain.PostCategory;
+import org.wooriverygood.api.post.dto.NewPostRequest;
+import org.wooriverygood.api.post.repository.PostRepository;
+import org.wooriverygood.api.global.auth.AuthInfo;
+
+@Service
+@Transactional
+@RequiredArgsConstructor
+public class PostCreateService {
+
+ private final PostRepository postRepository;
+
+ private final MemberRepository memberRepository;
+
+ public void addPost(AuthInfo authInfo, NewPostRequest newPostRequest) {
+ Member member = memberRepository.findById(authInfo.getMemberId())
+ .orElseThrow(MemberNotFoundException::new);
+ PostCategory.parse(newPostRequest.getPostCategory());
+ Post post = createPost(member, newPostRequest);
+ postRepository.save(post);
+ }
+
+ private Post createPost(Member member, NewPostRequest newPostRequest) {
+ return Post.builder()
+ .title(newPostRequest.getPostTitle())
+ .content(newPostRequest.getPostContent())
+ .category(PostCategory.parse(newPostRequest.getPostCategory()))
+ .member(member)
+ .build();
+ }
+
+}
\ No newline at end of file
diff --git a/src/main/java/org/wooriverygood/api/post/application/PostDeleteService.java b/src/main/java/org/wooriverygood/api/post/application/PostDeleteService.java
new file mode 100644
index 0000000..feeed2c
--- /dev/null
+++ b/src/main/java/org/wooriverygood/api/post/application/PostDeleteService.java
@@ -0,0 +1,46 @@
+package org.wooriverygood.api.post.application;
+
+import lombok.RequiredArgsConstructor;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+import org.wooriverygood.api.comment.repository.CommentRepository;
+import org.wooriverygood.api.member.domain.Member;
+import org.wooriverygood.api.member.exception.MemberNotFoundException;
+import org.wooriverygood.api.member.repository.MemberRepository;
+import org.wooriverygood.api.post.domain.Post;
+import org.wooriverygood.api.post.exception.PostNotFoundException;
+import org.wooriverygood.api.post.repository.PostLikeRepository;
+import org.wooriverygood.api.post.repository.PostRepository;
+import org.wooriverygood.api.global.auth.AuthInfo;
+
+@Service
+@Transactional
+@RequiredArgsConstructor
+public class PostDeleteService {
+
+ private final PostRepository postRepository;
+
+ private final CommentRepository commentRepository;
+
+ private final PostLikeRepository postLikeRepository;
+
+ private final MemberRepository memberRepository;
+
+
+ public void deletePost(AuthInfo authInfo, long postId) {
+ Member member = memberRepository.findById(authInfo.getMemberId())
+ .orElseThrow(MemberNotFoundException::new);
+ Post post = postRepository.findById(postId)
+ .orElseThrow(PostNotFoundException::new);
+ post.validateAuthor(member);
+
+ deleteCommentAndPostLike(post);
+ postRepository.delete(post);
+ }
+
+ private void deleteCommentAndPostLike(Post post) {
+ commentRepository.deleteAllByPost(post);
+ postLikeRepository.deleteAllByPost(post);
+ }
+
+}
diff --git a/src/main/java/org/wooriverygood/api/post/application/PostFindService.java b/src/main/java/org/wooriverygood/api/post/application/PostFindService.java
new file mode 100644
index 0000000..76e5d92
--- /dev/null
+++ b/src/main/java/org/wooriverygood/api/post/application/PostFindService.java
@@ -0,0 +1,83 @@
+package org.wooriverygood.api.post.application;
+
+import lombok.RequiredArgsConstructor;
+import org.springframework.data.domain.Page;
+import org.springframework.data.domain.Pageable;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+import org.wooriverygood.api.member.domain.Member;
+import org.wooriverygood.api.member.exception.MemberNotFoundException;
+import org.wooriverygood.api.member.repository.MemberRepository;
+import org.wooriverygood.api.post.domain.Post;
+import org.wooriverygood.api.post.domain.PostCategory;
+import org.wooriverygood.api.post.dto.PostDetailResponse;
+import org.wooriverygood.api.post.dto.PostResponse;
+import org.wooriverygood.api.post.dto.PostsResponse;
+import org.wooriverygood.api.post.exception.PostNotFoundException;
+import org.wooriverygood.api.post.repository.PostLikeRepository;
+import org.wooriverygood.api.post.repository.PostRepository;
+import org.wooriverygood.api.global.auth.AuthInfo;
+
+import java.util.List;
+
+@Service
+@RequiredArgsConstructor
+@Transactional(readOnly = true)
+public class PostFindService {
+
+ private final PostRepository postRepository;
+
+ private final PostLikeRepository postLikeRepository;
+
+ private final MemberRepository memberRepository;
+
+
+ public PostsResponse findPosts(AuthInfo authInfo, Pageable pageable, String postCategory) {
+ Page page = findPostsPage(pageable, postCategory);
+ Member member = memberRepository.findById(authInfo.getMemberId())
+ .orElseThrow(MemberNotFoundException::new);
+ return convertToPostsResponse(member, page);
+ }
+
+ private Page findPostsPage(Pageable pageable, String postCategory) {
+ if (postCategory.isEmpty())
+ return postRepository.findAllByOrderByIdDesc(pageable);
+ PostCategory category = PostCategory.parse(postCategory);
+ return postRepository.findAllByCategoryOrderByIdDesc(category, pageable);
+ }
+
+ @Transactional
+ public PostDetailResponse findPostById(long postId, AuthInfo authInfo) {
+ Post post = postRepository.findById(postId)
+ .orElseThrow(PostNotFoundException::new);
+ Member member = memberRepository.findById(authInfo.getMemberId())
+ .orElseThrow(MemberNotFoundException::new);
+ boolean liked = postLikeRepository.existsByPostAndMember(post, member);
+ boolean isMine = post.getMember().equals(member);
+ postRepository.increaseViewCount(postId);
+ return PostDetailResponse.of(post, member.getId(), liked, isMine);
+ }
+
+ public PostsResponse findMyPosts(AuthInfo authInfo, Pageable pageable) {
+ Member member = memberRepository.findById(authInfo.getMemberId())
+ .orElseThrow(MemberNotFoundException::new);
+ Page page = postRepository.findByMemberOrderByIdDesc(member, pageable);
+ return convertToPostsResponse(member, page);
+ }
+
+ private PostsResponse convertToPostsResponse(Member member, Page page) {
+ List posts = page.getContent().stream()
+ .map(post -> {
+ boolean liked = postLikeRepository.existsByPostAndMember(post, member);
+ return PostResponse.of(post, liked, post.getMember().equals(member));
+ })
+ .toList();
+
+ return PostsResponse.builder()
+ .posts(posts)
+ .totalPageCount(page.getTotalPages())
+ .totalPostCount(page.getTotalElements())
+ .build();
+ }
+
+}
\ No newline at end of file
diff --git a/src/main/java/org/wooriverygood/api/post/application/PostLikeToggleService.java b/src/main/java/org/wooriverygood/api/post/application/PostLikeToggleService.java
new file mode 100644
index 0000000..29508ce
--- /dev/null
+++ b/src/main/java/org/wooriverygood/api/post/application/PostLikeToggleService.java
@@ -0,0 +1,72 @@
+package org.wooriverygood.api.post.application;
+
+import lombok.RequiredArgsConstructor;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+import org.wooriverygood.api.member.domain.Member;
+import org.wooriverygood.api.member.exception.MemberNotFoundException;
+import org.wooriverygood.api.member.repository.MemberRepository;
+import org.wooriverygood.api.post.domain.Post;
+import org.wooriverygood.api.post.domain.PostLike;
+import org.wooriverygood.api.post.dto.PostLikeResponse;
+import org.wooriverygood.api.post.exception.PostNotFoundException;
+import org.wooriverygood.api.post.repository.PostLikeRepository;
+import org.wooriverygood.api.post.repository.PostRepository;
+import org.wooriverygood.api.global.auth.AuthInfo;
+
+import java.util.Optional;
+
+@Service
+@Transactional
+@RequiredArgsConstructor
+public class PostLikeToggleService {
+
+ private final PostRepository postRepository;
+
+ private final PostLikeRepository postLikeRepository;
+
+ private final MemberRepository memberRepository;
+
+
+ public PostLikeResponse togglePostLike(long postId, AuthInfo authInfo) {
+ Member member = memberRepository.findById(authInfo.getMemberId())
+ .orElseThrow(MemberNotFoundException::new);
+ Post post = postRepository.findById(postId)
+ .orElseThrow(PostNotFoundException::new);
+
+ Optional postLike = postLikeRepository.findByPostAndMember(post, member);
+
+ if (postLike.isEmpty()) {
+ addPostLike(post, member);
+ return createPostLikeResponse(post, true);
+ }
+
+ deletePostLike(post, postLike.get());
+ return createPostLikeResponse(post, false);
+ }
+
+ private void addPostLike(Post post, Member member) {
+ PostLike newPostLike = PostLike.builder()
+ .post(post)
+ .member(member)
+ .build();
+
+ post.addPostLike(newPostLike);
+ postLikeRepository.save(newPostLike);
+ postRepository.increaseLikeCount(post.getId());
+ }
+
+ private void deletePostLike(Post post, PostLike postLike) {
+ post.deletePostLike(postLike);
+ postRepository.decreaseLikeCount(post.getId());
+ }
+
+ private PostLikeResponse createPostLikeResponse(Post post, boolean liked) {
+ int likeCount = post.getLikeCount() + (liked ? 1 : -1);
+ return PostLikeResponse.builder()
+ .likeCount(likeCount)
+ .liked(liked)
+ .build();
+ }
+
+}
\ No newline at end of file
diff --git a/src/main/java/org/wooriverygood/api/post/application/PostUpdateService.java b/src/main/java/org/wooriverygood/api/post/application/PostUpdateService.java
new file mode 100644
index 0000000..0978f36
--- /dev/null
+++ b/src/main/java/org/wooriverygood/api/post/application/PostUpdateService.java
@@ -0,0 +1,36 @@
+package org.wooriverygood.api.post.application;
+
+import lombok.RequiredArgsConstructor;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+import org.wooriverygood.api.member.domain.Member;
+import org.wooriverygood.api.member.exception.MemberNotFoundException;
+import org.wooriverygood.api.member.repository.MemberRepository;
+import org.wooriverygood.api.post.domain.Post;
+import org.wooriverygood.api.post.dto.PostUpdateRequest;
+import org.wooriverygood.api.post.exception.PostNotFoundException;
+import org.wooriverygood.api.post.repository.PostRepository;
+import org.wooriverygood.api.global.auth.AuthInfo;
+
+@Service
+@Transactional
+@RequiredArgsConstructor
+public class PostUpdateService {
+
+ private final PostRepository postRepository;
+
+ private final MemberRepository memberRepository;
+
+
+ public void updatePost(long postId, PostUpdateRequest postUpdateRequest, AuthInfo authInfo) {
+ Member member = memberRepository.findById(authInfo.getMemberId())
+ .orElseThrow(MemberNotFoundException::new);
+ Post post = postRepository.findById(postId)
+ .orElseThrow(PostNotFoundException::new);
+ post.validateAuthor(member);
+
+ post.updateTitle(postUpdateRequest.getPostTitle());
+ post.updateContent(postUpdateRequest.getPostContent());
+ }
+
+}
diff --git a/src/main/java/org/wooriverygood/api/post/controller/PostController.java b/src/main/java/org/wooriverygood/api/post/controller/PostController.java
deleted file mode 100644
index 54b5381..0000000
--- a/src/main/java/org/wooriverygood/api/post/controller/PostController.java
+++ /dev/null
@@ -1,88 +0,0 @@
-package org.wooriverygood.api.post.controller;
-
-import jakarta.validation.Valid;
-import org.springframework.data.domain.PageRequest;
-import org.springframework.data.domain.Pageable;
-import org.springframework.http.HttpStatus;
-import org.springframework.http.ResponseEntity;
-import org.springframework.web.bind.annotation.*;
-import org.wooriverygood.api.post.dto.*;
-import org.wooriverygood.api.post.service.PostService;
-import org.wooriverygood.api.support.AuthInfo;
-import org.wooriverygood.api.support.Login;
-
-
-@RestController
-@RequestMapping("/community")
-public class PostController {
-
- private final PostService postService;
-
- private final int PAGE_SIZE = 10;
-
-
- public PostController(PostService postService) {
- this.postService = postService;
- }
-
- @GetMapping
- public ResponseEntity findPosts(@Login AuthInfo authInfo,
- @RequestParam(required = false, defaultValue = "0", value = "page") int pageNo) {
- Pageable pageable = PageRequest.of(pageNo, PAGE_SIZE);
- PostsResponse response = postService.findPosts(authInfo, pageable);
- return ResponseEntity.ok(response);
- }
-
- @GetMapping("/category/{category}")
- public ResponseEntity findPostsByCategory(@Login AuthInfo authInfo,
- @RequestParam(required = false, defaultValue = "0", value = "page") int pageNo,
- @PathVariable("category") String postCategory) {
- Pageable pageable = PageRequest.of(pageNo, PAGE_SIZE);
- PostsResponse response = postService.findPostsByCategory(authInfo, pageable, postCategory);
- return ResponseEntity.ok(response);
- }
-
- @GetMapping("/{id}")
- public ResponseEntity findPostById(@PathVariable("id") Long postId,
- @Login AuthInfo authInfo) {
- PostResponse postResponse = postService.findPostById(postId, authInfo);
- return ResponseEntity.ok(postResponse);
- }
-
- @PostMapping
- public ResponseEntity addPost(@Login AuthInfo authInfo,
- @Valid @RequestBody NewPostRequest newPostRequest) {
- NewPostResponse response = postService.addPost(authInfo, newPostRequest);
- return ResponseEntity.status(HttpStatus.CREATED).body(response);
- }
-
- @GetMapping("/me")
- public ResponseEntity findMyPosts(@Login AuthInfo authInfo,
- @RequestParam(required = false, defaultValue = "0", value = "page") int pageNo) {
- Pageable pageable = PageRequest.of(pageNo, PAGE_SIZE);
- PostsResponse response = postService.findMyPosts(authInfo, pageable);
- return ResponseEntity.ok(response);
- }
-
- @PutMapping("/{id}/like")
- public ResponseEntity likePost(@PathVariable("id") Long postId,
- @Login AuthInfo authInfo) {
- PostLikeResponse response = postService.likePost(postId, authInfo);
- return ResponseEntity.ok(response);
- }
-
- @PutMapping("/{id}")
- public ResponseEntity updatePost(@PathVariable("id") Long postId,
- @Valid @RequestBody PostUpdateRequest postUpdateRequest,
- @Login AuthInfo authInfo) {
- PostUpdateResponse response = postService.updatePost(postId, postUpdateRequest, authInfo);
- return ResponseEntity.ok(response);
- }
-
- @DeleteMapping("/{id}")
- public ResponseEntity deletePost(@PathVariable("id") Long postId,
- @Login AuthInfo authInfo) {
- PostDeleteResponse response = postService.deletePost(postId, authInfo);
- return ResponseEntity.ok(response);
- }
-}
diff --git a/src/main/java/org/wooriverygood/api/post/domain/Content.java b/src/main/java/org/wooriverygood/api/post/domain/Content.java
new file mode 100644
index 0000000..0d4dc38
--- /dev/null
+++ b/src/main/java/org/wooriverygood/api/post/domain/Content.java
@@ -0,0 +1,32 @@
+package org.wooriverygood.api.post.domain;
+
+import jakarta.persistence.Column;
+import jakarta.persistence.Embeddable;
+import lombok.Getter;
+import org.wooriverygood.api.post.exception.InvalidPostContentException;
+
+@Embeddable
+@Getter
+public class Content {
+
+ private static final int MAX_CONTENT_LENGTH = 2000;
+
+ @Column(name = "post_content", length = MAX_CONTENT_LENGTH)
+ private String value;
+
+
+ protected Content() {
+ }
+
+ public Content(String value) {
+ validate(value);
+ this.value = value;
+ }
+
+ private void validate(String value) {
+ if (value.length() > MAX_CONTENT_LENGTH) {
+ throw new InvalidPostContentException();
+ }
+ }
+
+}
diff --git a/src/main/java/org/wooriverygood/api/post/domain/Post.java b/src/main/java/org/wooriverygood/api/post/domain/Post.java
index 20ae39c..ae1b951 100644
--- a/src/main/java/org/wooriverygood/api/post/domain/Post.java
+++ b/src/main/java/org/wooriverygood/api/post/domain/Post.java
@@ -1,24 +1,24 @@
package org.wooriverygood.api.post.domain;
import jakarta.persistence.*;
-import lombok.Builder;
-import lombok.Getter;
-import lombok.NoArgsConstructor;
+import lombok.*;
import org.hibernate.annotations.ColumnDefault;
import org.springframework.data.annotation.CreatedDate;
import org.springframework.data.jpa.domain.support.AuditingEntityListener;
-import org.wooriverygood.api.advice.exception.AuthorizationException;
+import org.wooriverygood.api.global.error.exception.AuthorizationException;
import org.wooriverygood.api.comment.domain.Comment;
+import org.wooriverygood.api.member.domain.Member;
import org.wooriverygood.api.report.domain.PostReport;
import java.time.LocalDateTime;
+import java.util.ArrayList;
import java.util.List;
@Entity
-@Table(name = "posts")
@Getter
+@Table(name = "posts")
@EntityListeners(AuditingEntityListener.class)
-@NoArgsConstructor
+@NoArgsConstructor(access = AccessLevel.PROTECTED)
public class Post {
@Id
@@ -30,32 +30,36 @@ public class Post {
@Column(name = "post_category")
private PostCategory category;
- @Column(name = "post_title", length = 45, nullable = false)
- private String title;
+ @Embedded
+ private Title title;
- @Column(name = "post_content", length = 2000)
- private String content;
+ @Embedded
+ private Content content;
- @Column(name = "post_author", length = 1000)
- private String author;
+ @ManyToOne(fetch = FetchType.LAZY)
+ private Member member;
- @OneToMany(mappedBy = "post")
- private List comments;
+ @OneToMany(mappedBy = "post", fetch = FetchType.EAGER)
+ private List comments = new ArrayList<>();
@OneToMany(mappedBy = "post", cascade = CascadeType.ALL, orphanRemoval = true)
- private List postLikes;
+ private List postLikes = new ArrayList<>();
@Column(name = "like_count")
@ColumnDefault("0")
private int likeCount;
@OneToMany(mappedBy = "post")
- private List reports;
+ private List reports = new ArrayList<>();
@Column(name = "report_count")
@ColumnDefault("0")
private int reportCount;
+ @Column(name = "view_count")
+ @ColumnDefault("0")
+ private int viewCount;
+
@ColumnDefault("false")
private boolean updated;
@@ -65,20 +69,22 @@ public class Post {
@Builder
- public Post(Long id, PostCategory category, String title, String content, String author, List comments, List postLikes, List reports, boolean updated) {
+ public Post(Long id, PostCategory category, String title, String content,
+ Member member, List comments, List postLikes,
+ List reports, boolean updated) {
this.id = id;
this.category = category;
- this.title = title;
- this.content = content;
- this.author = author;
+ this.title = new Title(title);
+ this.content = new Content(content);
+ this.member = member;
this.comments = comments;
this.postLikes = postLikes;
this.reports = reports;
this.updated = updated;
}
- public void validateAuthor(String author) {
- if (!this.author.equals(author)) throw new AuthorizationException();
+ public void validateAuthor(Member member) {
+ this.member.verify(member);
}
public void addPostLike(PostLike postLike) {
@@ -91,7 +97,7 @@ public void deletePostLike(PostLike postLike) {
}
public void updateTitle(String title) {
- this.title = title;
+ this.title = new Title(title);
updated = true;
}
@@ -100,7 +106,7 @@ public void addReport(PostReport report) {
}
public void updateContent(String content) {
- this.content = content;
+ this.content = new Content(content);
updated = true;
}
@@ -110,11 +116,23 @@ public int getCommentCount() {
return comments.size();
}
- public boolean hasReportByUser(String username) {
+ public boolean hasReportByMember(Member member) {
for (PostReport report: reports)
- if (report.isOwner(username))
+ if (report.isOwner(member))
return true;
return false;
}
+ public String getTitle() {
+ return isReportedTooMuch() ? null : title.getValue();
+ }
+
+ public String getContent() {
+ return isReportedTooMuch() ? null : content.getValue();
+ }
+
+ public boolean isReportedTooMuch() {
+ return reportCount >= 5;
+ }
+
}
diff --git a/src/main/java/org/wooriverygood/api/post/domain/PostCategory.java b/src/main/java/org/wooriverygood/api/post/domain/PostCategory.java
index 925a096..e2df158 100644
--- a/src/main/java/org/wooriverygood/api/post/domain/PostCategory.java
+++ b/src/main/java/org/wooriverygood/api/post/domain/PostCategory.java
@@ -1,7 +1,7 @@
package org.wooriverygood.api.post.domain;
import lombok.Getter;
-import org.wooriverygood.api.advice.exception.InvalidPostCategoryException;
+import org.wooriverygood.api.post.exception.InvalidPostCategoryException;
@Getter
public enum PostCategory {
diff --git a/src/main/java/org/wooriverygood/api/post/domain/PostLike.java b/src/main/java/org/wooriverygood/api/post/domain/PostLike.java
index 728decc..fd74461 100644
--- a/src/main/java/org/wooriverygood/api/post/domain/PostLike.java
+++ b/src/main/java/org/wooriverygood/api/post/domain/PostLike.java
@@ -1,14 +1,13 @@
package org.wooriverygood.api.post.domain;
import jakarta.persistence.*;
-import lombok.Builder;
-import lombok.Getter;
-import lombok.NoArgsConstructor;
+import lombok.*;
+import org.wooriverygood.api.member.domain.Member;
@Entity
-@Table(name = "postLikes")
@Getter
-@NoArgsConstructor
+@Table(name = "post_likes")
+@NoArgsConstructor(access = AccessLevel.PROTECTED)
public class PostLike {
@Id
@@ -19,16 +18,17 @@ public class PostLike {
@JoinColumn(name = "post_id", referencedColumnName = "post_id")
private Post post;
- @Column
- private String username;
+ @ManyToOne(fetch = FetchType.LAZY)
+ private Member member;
@Builder
- public PostLike(Long id, Post post, String username) {
+ public PostLike(Long id, Post post, Member member) {
this.id = id;
this.post = post;
- this.username = username;
+ this.member = member;
}
+
public void delete() {
this.post = null;
}
diff --git a/src/main/java/org/wooriverygood/api/post/domain/Title.java b/src/main/java/org/wooriverygood/api/post/domain/Title.java
new file mode 100644
index 0000000..77070e6
--- /dev/null
+++ b/src/main/java/org/wooriverygood/api/post/domain/Title.java
@@ -0,0 +1,35 @@
+package org.wooriverygood.api.post.domain;
+
+import jakarta.persistence.Column;
+import jakarta.persistence.Embeddable;
+import lombok.AccessLevel;
+import lombok.Getter;
+import lombok.NoArgsConstructor;
+import org.wooriverygood.api.post.exception.InvalidPostContentException;
+import org.wooriverygood.api.post.exception.InvalidPostTitleException;
+
+@Getter
+@Embeddable
+@NoArgsConstructor(access = AccessLevel.PROTECTED)
+public class Title {
+
+ private static final int MAX_TITLE_LENGTH = 45;
+
+ @Column(name = "post_title", length = MAX_TITLE_LENGTH, nullable = false)
+ private String value;
+
+
+ public Title(String value) {
+ validate(value);
+ this.value = value;
+ }
+
+ private void validate(String value) {
+ if (value == null || value.isBlank())
+ throw new InvalidPostTitleException();
+
+ if (value.length() > MAX_TITLE_LENGTH)
+ throw new InvalidPostContentException();
+ }
+
+}
diff --git a/src/main/java/org/wooriverygood/api/post/dto/NewPostRequest.java b/src/main/java/org/wooriverygood/api/post/dto/NewPostRequest.java
index 7e9c03c..af57cbb 100644
--- a/src/main/java/org/wooriverygood/api/post/dto/NewPostRequest.java
+++ b/src/main/java/org/wooriverygood/api/post/dto/NewPostRequest.java
@@ -1,21 +1,28 @@
package org.wooriverygood.api.post.dto;
+import com.fasterxml.jackson.databind.PropertyNamingStrategies;
+import com.fasterxml.jackson.databind.annotation.JsonNaming;
import jakarta.validation.constraints.NotBlank;
import lombok.Builder;
import lombok.Getter;
@Getter
+@JsonNaming(PropertyNamingStrategies.SnakeCaseStrategy.class)
public class NewPostRequest {
@NotBlank(message = "제목을 비우면 안됩니다.")
- private final String post_title;
- private final String post_category;
- private final String post_content;
+ private final String postTitle;
+
+ private final String postCategory;
+
+ private final String postContent;
+
@Builder
- public NewPostRequest(String post_title, String post_category, String post_content) {
- this.post_title = post_title;
- this.post_category = post_category;
- this.post_content = post_content;
+ public NewPostRequest(String postTitle, String postCategory, String postContent) {
+ this.postTitle = postTitle;
+ this.postCategory = postCategory;
+ this.postContent = postContent;
}
+
}
diff --git a/src/main/java/org/wooriverygood/api/post/dto/NewPostResponse.java b/src/main/java/org/wooriverygood/api/post/dto/NewPostResponse.java
deleted file mode 100644
index 34f97ea..0000000
--- a/src/main/java/org/wooriverygood/api/post/dto/NewPostResponse.java
+++ /dev/null
@@ -1,22 +0,0 @@
-package org.wooriverygood.api.post.dto;
-
-import lombok.Builder;
-import lombok.Getter;
-import org.wooriverygood.api.post.domain.PostCategory;
-
-@Getter
-public class NewPostResponse {
-
- private final Long post_id;
- private final String title;
- private final String category;
- private final String author;
-
- @Builder
- public NewPostResponse(Long post_id, String title, String category, String author) {
- this.post_id = post_id;
- this.title = title;
- this.category = category;
- this.author = author;
- }
-}
diff --git a/src/main/java/org/wooriverygood/api/post/dto/PostDeleteResponse.java b/src/main/java/org/wooriverygood/api/post/dto/PostDeleteResponse.java
deleted file mode 100644
index ebeb733..0000000
--- a/src/main/java/org/wooriverygood/api/post/dto/PostDeleteResponse.java
+++ /dev/null
@@ -1,16 +0,0 @@
-package org.wooriverygood.api.post.dto;
-
-import lombok.Builder;
-import lombok.Getter;
-
-@Getter
-public class PostDeleteResponse {
-
- private final Long post_id;
-
- @Builder
- public PostDeleteResponse(Long post_id) {
- this.post_id = post_id;
- }
-
-}
diff --git a/src/main/java/org/wooriverygood/api/post/dto/PostDetailResponse.java b/src/main/java/org/wooriverygood/api/post/dto/PostDetailResponse.java
new file mode 100644
index 0000000..d6c4af1
--- /dev/null
+++ b/src/main/java/org/wooriverygood/api/post/dto/PostDetailResponse.java
@@ -0,0 +1,80 @@
+package org.wooriverygood.api.post.dto;
+
+import com.fasterxml.jackson.databind.PropertyNamingStrategies;
+import com.fasterxml.jackson.databind.annotation.JsonNaming;
+import lombok.Builder;
+import lombok.Getter;
+import org.wooriverygood.api.post.domain.Post;
+
+import java.time.LocalDateTime;
+
+@Getter
+@JsonNaming(PropertyNamingStrategies.SnakeCaseStrategy.class)
+public class PostDetailResponse {
+
+ private final Long postId;
+
+ private final String postTitle;
+
+ private final String postContent;
+
+ private final String postCategory;
+
+ private final long memberId;
+
+ private final boolean isMine;
+
+ private final int postComments;
+
+ private final int postLikes;
+
+ private final LocalDateTime postTime;
+
+ private final boolean liked;
+
+ private final boolean updated;
+
+ private final boolean reported;
+
+ private final int viewCount;
+
+
+ @Builder
+ public PostDetailResponse(Long postId, String postTitle, String postContent,
+ String postCategory, long memberId, int postComments,
+ int postLikes, LocalDateTime postTime, boolean liked,
+ boolean isMine, boolean updated, boolean reported, int viewCount) {
+ this.postId = postId;
+ this.postTitle = postTitle;
+ this.postContent = postContent;
+ this.postCategory = postCategory;
+ this.memberId = memberId;
+ this.isMine = isMine;
+ this.postComments = postComments;
+ this.postLikes = postLikes;
+ this.postTime = postTime;
+ this.liked = liked;
+ this.updated = updated;
+ this.reported = reported;
+ this.viewCount = viewCount;
+ }
+
+ public static PostDetailResponse of(Post post, long memberId, boolean liked, boolean isMine) {
+ return PostDetailResponse.builder()
+ .postId(post.getId())
+ .postTitle(post.getTitle())
+ .postContent(post.getContent())
+ .postCategory(post.getCategory().getValue())
+ .isMine(isMine)
+ .memberId(memberId)
+ .postComments(post.getCommentCount())
+ .postLikes(post.getLikeCount())
+ .postTime(post.getCreatedAt())
+ .liked(liked)
+ .updated(post.isUpdated())
+ .reported(post.isReportedTooMuch())
+ .viewCount(post.getViewCount())
+ .build();
+ }
+
+}
diff --git a/src/main/java/org/wooriverygood/api/post/dto/PostLikeResponse.java b/src/main/java/org/wooriverygood/api/post/dto/PostLikeResponse.java
index 1108ab7..fd5b3c1 100644
--- a/src/main/java/org/wooriverygood/api/post/dto/PostLikeResponse.java
+++ b/src/main/java/org/wooriverygood/api/post/dto/PostLikeResponse.java
@@ -1,19 +1,22 @@
package org.wooriverygood.api.post.dto;
+import com.fasterxml.jackson.databind.PropertyNamingStrategies;
+import com.fasterxml.jackson.databind.annotation.JsonNaming;
import lombok.Builder;
import lombok.Getter;
@Getter
+@JsonNaming(PropertyNamingStrategies.SnakeCaseStrategy.class)
public class PostLikeResponse {
- private final int like_count;
+ private final int likeCount;
private final boolean liked;
@Builder
- public PostLikeResponse(int like_count, boolean liked) {
- this.like_count = like_count;
+ public PostLikeResponse(int likeCount, boolean liked) {
+ this.likeCount = likeCount;
this.liked = liked;
}
}
diff --git a/src/main/java/org/wooriverygood/api/post/dto/PostResponse.java b/src/main/java/org/wooriverygood/api/post/dto/PostResponse.java
index 6c45604..7b4c21d 100644
--- a/src/main/java/org/wooriverygood/api/post/dto/PostResponse.java
+++ b/src/main/java/org/wooriverygood/api/post/dto/PostResponse.java
@@ -1,5 +1,7 @@
package org.wooriverygood.api.post.dto;
+import com.fasterxml.jackson.databind.PropertyNamingStrategies;
+import com.fasterxml.jackson.databind.annotation.JsonNaming;
import lombok.Builder;
import lombok.Getter;
import org.wooriverygood.api.post.domain.Post;
@@ -7,23 +9,20 @@
import java.time.LocalDateTime;
@Getter
+@JsonNaming(PropertyNamingStrategies.SnakeCaseStrategy.class)
public class PostResponse {
- private final Long post_id;
+ private final Long postId;
- private final String post_title;
+ private final String postTitle;
- private final String post_content;
+ private final String postCategory;
- private final String post_category;
+ private final int postComments;
- private final String post_author;
+ private final int postLikes;
- private final int post_comments;
-
- private final int post_likes;
-
- private final LocalDateTime post_time;
+ private final LocalDateTime postTime;
private final boolean liked;
@@ -31,36 +30,43 @@ public class PostResponse {
private final boolean reported;
+ private final int viewCount;
+
+ private final boolean isMine;
+
+ private final long memberId;
+
@Builder
- public PostResponse(Long post_id, String post_title, String post_content, String post_category, String post_author, int post_comments, int post_likes, LocalDateTime post_time, boolean liked, boolean updated, boolean reported) {
- this.post_id = post_id;
- this.post_title = post_title;
- this.post_content = post_content;
- this.post_category = post_category;
- this.post_author = post_author;
- this.post_comments = post_comments;
- this.post_likes = post_likes;
- this.post_time = post_time;
+ public PostResponse(Long postId, String postTitle, String postCategory, int postComments, int postLikes, LocalDateTime postTime, boolean liked, boolean updated, boolean reported, int viewCount, boolean isMine, long memberId) {
+ this.postId = postId;
+ this.postTitle = postTitle;
+ this.postCategory = postCategory;
+ this.postComments = postComments;
+ this.postLikes = postLikes;
+ this.postTime = postTime;
this.liked = liked;
this.updated = updated;
this.reported = reported;
+ this.viewCount = viewCount;
+ this.isMine = isMine;
+ this.memberId = memberId;
}
- public static PostResponse from(Post post, boolean liked) {
- boolean reported = post.getReportCount() >= 5;
+ public static PostResponse of(Post post, boolean liked, boolean isMine) {
return PostResponse.builder()
- .post_id(post.getId())
- .post_title(reported ? null : post.getTitle())
- .post_content(reported ? null : post.getContent())
- .post_category(post.getCategory().getValue())
- .post_author(post.getAuthor())
- .post_comments(post.getCommentCount())
- .post_likes(post.getLikeCount())
- .post_time(post.getCreatedAt())
+ .postId(post.getId())
+ .postTitle(post.getTitle())
+ .postCategory(post.getCategory().getValue())
+ .postComments(post.getCommentCount())
+ .postLikes(post.getLikeCount())
+ .postTime(post.getCreatedAt())
.liked(liked)
.updated(post.isUpdated())
- .reported(reported)
+ .reported(post.isReportedTooMuch())
+ .viewCount(post.getViewCount())
+ .memberId(post.getMember().getId())
+ .isMine(isMine)
.build();
}
diff --git a/src/main/java/org/wooriverygood/api/post/dto/PostUpdateRequest.java b/src/main/java/org/wooriverygood/api/post/dto/PostUpdateRequest.java
index eba4da9..e0c8865 100644
--- a/src/main/java/org/wooriverygood/api/post/dto/PostUpdateRequest.java
+++ b/src/main/java/org/wooriverygood/api/post/dto/PostUpdateRequest.java
@@ -1,22 +1,25 @@
package org.wooriverygood.api.post.dto;
+import com.fasterxml.jackson.databind.PropertyNamingStrategies;
+import com.fasterxml.jackson.databind.annotation.JsonNaming;
import jakarta.validation.constraints.NotBlank;
import lombok.Builder;
import lombok.Getter;
-import lombok.NoArgsConstructor;
@Getter
-@NoArgsConstructor
+@JsonNaming(PropertyNamingStrategies.SnakeCaseStrategy.class)
public class PostUpdateRequest {
@NotBlank(message = "제목이 없습니다.")
- private String post_title;
+ private String postTitle;
+
+ private String postContent;
- private String post_content;
@Builder
- public PostUpdateRequest(String post_title, String post_content) {
- this.post_title = post_title;
- this.post_content = post_content;
+ public PostUpdateRequest(String postTitle, String postContent) {
+ this.postTitle = postTitle;
+ this.postContent = postContent;
}
+
}
diff --git a/src/main/java/org/wooriverygood/api/post/dto/PostUpdateResponse.java b/src/main/java/org/wooriverygood/api/post/dto/PostUpdateResponse.java
deleted file mode 100644
index 8556b4d..0000000
--- a/src/main/java/org/wooriverygood/api/post/dto/PostUpdateResponse.java
+++ /dev/null
@@ -1,16 +0,0 @@
-package org.wooriverygood.api.post.dto;
-
-import lombok.Builder;
-import lombok.Getter;
-
-@Getter
-public class PostUpdateResponse {
-
- private final Long post_id;
-
-
- @Builder
- public PostUpdateResponse(Long post_id) {
- this.post_id = post_id;
- }
-}
diff --git a/src/main/java/org/wooriverygood/api/advice/exception/InvalidPostCategoryException.java b/src/main/java/org/wooriverygood/api/post/exception/InvalidPostCategoryException.java
similarity index 65%
rename from src/main/java/org/wooriverygood/api/advice/exception/InvalidPostCategoryException.java
rename to src/main/java/org/wooriverygood/api/post/exception/InvalidPostCategoryException.java
index 69a474e..7a8939a 100644
--- a/src/main/java/org/wooriverygood/api/advice/exception/InvalidPostCategoryException.java
+++ b/src/main/java/org/wooriverygood/api/post/exception/InvalidPostCategoryException.java
@@ -1,6 +1,6 @@
-package org.wooriverygood.api.advice.exception;
+package org.wooriverygood.api.post.exception;
-import org.wooriverygood.api.advice.exception.general.BadRequestException;
+import org.wooriverygood.api.global.error.exception.BadRequestException;
public class InvalidPostCategoryException extends BadRequestException {
diff --git a/src/main/java/org/wooriverygood/api/post/exception/InvalidPostContentException.java b/src/main/java/org/wooriverygood/api/post/exception/InvalidPostContentException.java
new file mode 100644
index 0000000..c5eb1f3
--- /dev/null
+++ b/src/main/java/org/wooriverygood/api/post/exception/InvalidPostContentException.java
@@ -0,0 +1,12 @@
+package org.wooriverygood.api.post.exception;
+
+import org.wooriverygood.api.global.error.exception.BadRequestException;
+
+public class InvalidPostContentException extends BadRequestException {
+
+ private static final String MESSAGE = "게시글 내용이 유효하지 않습니다.";
+
+ public InvalidPostContentException() {
+ super(MESSAGE);
+ }
+}
diff --git a/src/main/java/org/wooriverygood/api/post/exception/InvalidPostTitleException.java b/src/main/java/org/wooriverygood/api/post/exception/InvalidPostTitleException.java
new file mode 100644
index 0000000..dcd787d
--- /dev/null
+++ b/src/main/java/org/wooriverygood/api/post/exception/InvalidPostTitleException.java
@@ -0,0 +1,12 @@
+package org.wooriverygood.api.post.exception;
+
+import org.wooriverygood.api.global.error.exception.BadRequestException;
+
+public class InvalidPostTitleException extends BadRequestException {
+
+ private static final String MESSAGE = "게시글 제목이 유효하지 않습니다.";
+
+ public InvalidPostTitleException() {
+ super(MESSAGE);
+ }
+}
diff --git a/src/main/java/org/wooriverygood/api/advice/exception/PostNotFoundException.java b/src/main/java/org/wooriverygood/api/post/exception/PostNotFoundException.java
similarity index 64%
rename from src/main/java/org/wooriverygood/api/advice/exception/PostNotFoundException.java
rename to src/main/java/org/wooriverygood/api/post/exception/PostNotFoundException.java
index 16e945b..d4073b6 100644
--- a/src/main/java/org/wooriverygood/api/advice/exception/PostNotFoundException.java
+++ b/src/main/java/org/wooriverygood/api/post/exception/PostNotFoundException.java
@@ -1,6 +1,6 @@
-package org.wooriverygood.api.advice.exception;
+package org.wooriverygood.api.post.exception;
-import org.wooriverygood.api.advice.exception.general.NotFoundException;
+import org.wooriverygood.api.global.error.exception.NotFoundException;
public class PostNotFoundException extends NotFoundException {
diff --git a/src/main/java/org/wooriverygood/api/post/repository/PostLikeRepository.java b/src/main/java/org/wooriverygood/api/post/repository/PostLikeRepository.java
index b8f9a0a..f1ff129 100644
--- a/src/main/java/org/wooriverygood/api/post/repository/PostLikeRepository.java
+++ b/src/main/java/org/wooriverygood/api/post/repository/PostLikeRepository.java
@@ -1,6 +1,7 @@
package org.wooriverygood.api.post.repository;
import org.springframework.data.jpa.repository.JpaRepository;
+import org.wooriverygood.api.member.domain.Member;
import org.wooriverygood.api.post.domain.Post;
import org.wooriverygood.api.post.domain.PostLike;
@@ -8,9 +9,9 @@
public interface PostLikeRepository extends JpaRepository {
- Optional findByPostAndUsername(Post post, String username);
+ Optional findByPostAndMember(Post post, Member member);
- boolean existsByPostAndUsername(Post post, String username);
+ boolean existsByPostAndMember(Post post, Member member);
void deleteAllByPost(Post post);
}
diff --git a/src/main/java/org/wooriverygood/api/post/repository/PostRepository.java b/src/main/java/org/wooriverygood/api/post/repository/PostRepository.java
index 7f7f04c..12aa857 100644
--- a/src/main/java/org/wooriverygood/api/post/repository/PostRepository.java
+++ b/src/main/java/org/wooriverygood/api/post/repository/PostRepository.java
@@ -6,6 +6,7 @@
import org.springframework.data.jpa.repository.Modifying;
import org.springframework.data.jpa.repository.Query;
import org.springframework.transaction.annotation.Transactional;
+import org.wooriverygood.api.member.domain.Member;
import org.wooriverygood.api.post.domain.Post;
import org.wooriverygood.api.post.domain.PostCategory;
@@ -16,7 +17,7 @@ public interface PostRepository extends JpaRepository {
Page findAllByCategoryOrderByIdDesc(PostCategory category, Pageable pageable);
- Page findByAuthorOrderByIdDesc(String author, Pageable pageable);
+ Page findByMemberOrderByIdDesc(Member member, Pageable pageable);
@Transactional
@Modifying(clearAutomatically = true)
@@ -32,4 +33,10 @@ public interface PostRepository extends JpaRepository {
@Modifying(clearAutomatically = true)
@Query(value = "UPDATE posts SET report_count = report_count + 1 WHERE post_id = :postId", nativeQuery = true)
void increaseReportCount(Long postId);
+
+ @Transactional
+ @Modifying(clearAutomatically = true)
+ @Query(value = "UPDATE posts SET view_count = view_count + 1 WHERE post_id = :postId", nativeQuery = true)
+ void increaseViewCount(Long postId);
+
}
diff --git a/src/main/java/org/wooriverygood/api/post/service/PostService.java b/src/main/java/org/wooriverygood/api/post/service/PostService.java
deleted file mode 100644
index 1032e39..0000000
--- a/src/main/java/org/wooriverygood/api/post/service/PostService.java
+++ /dev/null
@@ -1,172 +0,0 @@
-package org.wooriverygood.api.post.service;
-
-import org.springframework.data.domain.Page;
-import org.springframework.data.domain.Pageable;
-import org.springframework.stereotype.Service;
-import org.springframework.transaction.annotation.Transactional;
-import org.wooriverygood.api.advice.exception.InvalidPostCategoryException;
-import org.wooriverygood.api.advice.exception.PostNotFoundException;
-import org.wooriverygood.api.comment.repository.CommentRepository;
-import org.wooriverygood.api.post.domain.Post;
-import org.wooriverygood.api.post.domain.PostCategory;
-import org.wooriverygood.api.post.domain.PostLike;
-import org.wooriverygood.api.post.dto.*;
-import org.wooriverygood.api.post.repository.PostLikeRepository;
-import org.wooriverygood.api.post.repository.PostRepository;
-import org.wooriverygood.api.support.AuthInfo;
-
-import java.util.List;
-import java.util.Optional;
-
-@Service
-@Transactional(readOnly = true)
-public class PostService {
-
- private final PostRepository postRepository;
-
- private final PostLikeRepository postLikeRepository;
-
- private final CommentRepository commentRepository;
-
-
-
- public PostService(PostRepository postRepository, PostLikeRepository postLikeRepository, CommentRepository commentRepository) {
- this.postRepository = postRepository;
- this.postLikeRepository = postLikeRepository;
- this.commentRepository = commentRepository;
- }
-
- public PostsResponse findPosts(AuthInfo authInfo, Pageable pageable) {
- Page page = postRepository.findAllByOrderByIdDesc(pageable);
- return convertToPostsResponse(authInfo, page);
- }
-
- public PostResponse findPostById(Long postId, AuthInfo authInfo) {
- Post post = postRepository.findById(postId)
- .orElseThrow(PostNotFoundException::new);
- boolean liked = postLikeRepository.existsByPostAndUsername(post, authInfo.getUsername());
- return PostResponse.from(post, liked);
- }
-
- @Transactional
- public NewPostResponse addPost(AuthInfo authInfo, NewPostRequest newPostRequest) {
- PostCategory.parse(newPostRequest.getPost_category());
- Post post = createPost(authInfo, newPostRequest);
- Post saved = postRepository.save(post);
- return createResponse(saved);
- }
-
- private Post createPost(AuthInfo authInfo, NewPostRequest newPostRequest) {
- return Post.builder()
- .title(newPostRequest.getPost_title())
- .content(newPostRequest.getPost_content())
- .category(PostCategory.parse(newPostRequest.getPost_category()))
- .author(authInfo.getUsername())
- .build();
- }
-
- private NewPostResponse createResponse(Post post) {
- return NewPostResponse.builder()
- .post_id(post.getId())
- .title(post.getTitle())
- .category(post.getCategory().getValue())
- .author(post.getAuthor())
- .build();
- }
-
- public PostsResponse findMyPosts(AuthInfo authInfo, Pageable pageable) {
- Page page = postRepository.findByAuthorOrderByIdDesc(authInfo.getUsername(), pageable);
- return convertToPostsResponse(authInfo, page);
- }
-
- private PostsResponse convertToPostsResponse(AuthInfo authInfo, Page page) {
- List posts = page.getContent().stream()
- .map(post -> {
- boolean liked = postLikeRepository.existsByPostAndUsername(post, authInfo.getUsername());
- return PostResponse.from(post, liked);
- })
- .toList();
-
- return PostsResponse.builder()
- .posts(posts)
- .totalPageCount(page.getTotalPages())
- .totalPostCount(page.getTotalElements())
- .build();
- }
-
- @Transactional
- public PostLikeResponse likePost(Long postId, AuthInfo authInfo) {
- Post post = postRepository.findById(postId)
- .orElseThrow(PostNotFoundException::new);
-
- Optional postLike = postLikeRepository.findByPostAndUsername(post, authInfo.getUsername());
-
- if (postLike.isEmpty()) {
- addPostLike(post, authInfo.getUsername());
- return createPostLikeResponse(post, true);
- }
-
- deletePostLike(post, postLike.get());
- return createPostLikeResponse(post, false);
- }
-
- private void addPostLike(Post post, String username) {
- PostLike newPostLike = PostLike.builder()
- .post(post)
- .username(username)
- .build();
-
- post.addPostLike(newPostLike);
- postLikeRepository.save(newPostLike);
- postRepository.increaseLikeCount(post.getId());
- }
-
- private void deletePostLike(Post post, PostLike postLike) {
- post.deletePostLike(postLike);
- postRepository.decreaseLikeCount(post.getId());
- }
-
- private PostLikeResponse createPostLikeResponse(Post post, boolean liked) {
- int likeCount = post.getLikeCount() + (liked ? 1 : -1);
- return PostLikeResponse.builder()
- .like_count(likeCount)
- .liked(liked)
- .build();
- }
-
- @Transactional
- public PostUpdateResponse updatePost(Long postId, PostUpdateRequest postUpdateRequest, AuthInfo authInfo) {
- Post post = postRepository.findById(postId)
- .orElseThrow(PostNotFoundException::new);
- post.validateAuthor(authInfo.getUsername());
-
- post.updateTitle(postUpdateRequest.getPost_title());
- post.updateContent(postUpdateRequest.getPost_content());
-
- return PostUpdateResponse.builder()
- .post_id(post.getId())
- .build();
- }
-
- @Transactional
- public PostDeleteResponse deletePost(Long postId, AuthInfo authInfo) {
- Post post = postRepository.findById(postId)
- .orElseThrow(PostNotFoundException::new);
- post.validateAuthor(authInfo.getUsername());
-
- commentRepository.deleteAllByPost(post);
- postLikeRepository.deleteAllByPost(post);
-
- postRepository.delete(post);
-
- return PostDeleteResponse.builder()
- .post_id(postId)
- .build();
- }
-
- public PostsResponse findPostsByCategory(AuthInfo authInfo, Pageable pageable, String postCategory) {
- PostCategory category = PostCategory.parse(postCategory);
- Page page = postRepository.findAllByCategoryOrderByIdDesc(category, pageable);
- return convertToPostsResponse(authInfo, page);
- }
-}
diff --git a/src/main/java/org/wooriverygood/api/report/controller/ReportController.java b/src/main/java/org/wooriverygood/api/report/api/ReportApi.java
similarity index 64%
rename from src/main/java/org/wooriverygood/api/report/controller/ReportController.java
rename to src/main/java/org/wooriverygood/api/report/api/ReportApi.java
index 5c3a927..4ad5b14 100644
--- a/src/main/java/org/wooriverygood/api/report/controller/ReportController.java
+++ b/src/main/java/org/wooriverygood/api/report/api/ReportApi.java
@@ -1,33 +1,33 @@
-package org.wooriverygood.api.report.controller;
+package org.wooriverygood.api.report.api;
import jakarta.validation.Valid;
+import lombok.RequiredArgsConstructor;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;
+import org.wooriverygood.api.report.application.PostReportService;
import org.wooriverygood.api.report.dto.ReportRequest;
-import org.wooriverygood.api.report.service.ReportService;
-import org.wooriverygood.api.support.AuthInfo;
-import org.wooriverygood.api.support.Login;
+import org.wooriverygood.api.report.application.CommentReportService;
+import org.wooriverygood.api.global.auth.AuthInfo;
+import org.wooriverygood.api.global.auth.Login;
@RestController
-public class ReportController {
+@RequiredArgsConstructor
+public class ReportApi {
- private final ReportService reportService;
+ private final CommentReportService commentReportService;
-
- public ReportController(ReportService reportService) {
- this.reportService = reportService;
- }
+ private final PostReportService postReportService;
@PostMapping("/posts/{id}/report")
public ResponseEntity reportPost(@PathVariable("id") Long postId,
@Valid @RequestBody ReportRequest request,
@Login AuthInfo authInfo) {
- reportService.reportPost(postId, request, authInfo);
+ postReportService.reportPost(postId, request, authInfo);
return ResponseEntity.status(HttpStatus.CREATED).build();
}
@@ -35,7 +35,7 @@ public ResponseEntity reportPost(@PathVariable("id") Long postId,
public ResponseEntity reportComment(@PathVariable("id") Long commentId,
@Valid @RequestBody ReportRequest request,
@Login AuthInfo authInfo) {
- reportService.reportComment(commentId, request, authInfo);
+ commentReportService.reportComment(commentId, request, authInfo);
return ResponseEntity.status(HttpStatus.CREATED).build();
}
}
diff --git a/src/main/java/org/wooriverygood/api/report/application/CommentReportService.java b/src/main/java/org/wooriverygood/api/report/application/CommentReportService.java
new file mode 100644
index 0000000..0984bcb
--- /dev/null
+++ b/src/main/java/org/wooriverygood/api/report/application/CommentReportService.java
@@ -0,0 +1,53 @@
+package org.wooriverygood.api.report.application;
+
+import lombok.RequiredArgsConstructor;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+import org.wooriverygood.api.comment.exception.CommentNotFoundException;
+import org.wooriverygood.api.member.domain.Member;
+import org.wooriverygood.api.member.exception.MemberNotFoundException;
+import org.wooriverygood.api.member.repository.MemberRepository;
+import org.wooriverygood.api.report.exception.DuplicatedCommentReportException;
+import org.wooriverygood.api.comment.domain.Comment;
+import org.wooriverygood.api.comment.repository.CommentRepository;
+import org.wooriverygood.api.report.domain.CommentReport;
+import org.wooriverygood.api.report.dto.ReportRequest;
+import org.wooriverygood.api.report.repository.CommentReportRepository;
+import org.wooriverygood.api.global.auth.AuthInfo;
+
+@Service
+@Transactional
+@RequiredArgsConstructor
+public class CommentReportService {
+
+ private final CommentRepository commentRepository;
+
+ private final CommentReportRepository commentReportRepository;
+
+ private final MemberRepository memberRepository;
+
+
+ public void reportComment(Long commentId, ReportRequest request, AuthInfo authInfo) {
+ Member member = memberRepository.findById(authInfo.getMemberId())
+ .orElseThrow(MemberNotFoundException::new);
+ Comment comment = commentRepository.findById(commentId)
+ .orElseThrow(CommentNotFoundException::new);
+
+ CommentReport report = CommentReport.builder()
+ .comment(comment)
+ .message(request.getMessage())
+ .member(member)
+ .build();
+
+ checkIfAlreadyReport(comment, member);
+ comment.addReport(report);
+ commentRepository.increaseReportCount(commentId);
+
+ commentReportRepository.save(report);
+ }
+
+ private void checkIfAlreadyReport(Comment comment, Member member) {
+ if (comment.hasReportByMember(member))
+ throw new DuplicatedCommentReportException();
+ }
+}
diff --git a/src/main/java/org/wooriverygood/api/report/application/PostReportService.java b/src/main/java/org/wooriverygood/api/report/application/PostReportService.java
new file mode 100644
index 0000000..20d64bf
--- /dev/null
+++ b/src/main/java/org/wooriverygood/api/report/application/PostReportService.java
@@ -0,0 +1,54 @@
+package org.wooriverygood.api.report.application;
+
+import lombok.RequiredArgsConstructor;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+import org.wooriverygood.api.member.domain.Member;
+import org.wooriverygood.api.member.exception.MemberNotFoundException;
+import org.wooriverygood.api.member.repository.MemberRepository;
+import org.wooriverygood.api.report.exception.DuplicatedPostReportException;
+import org.wooriverygood.api.global.auth.AuthInfo;
+import org.wooriverygood.api.post.domain.Post;
+import org.wooriverygood.api.post.exception.PostNotFoundException;
+import org.wooriverygood.api.post.repository.PostRepository;
+import org.wooriverygood.api.report.domain.PostReport;
+import org.wooriverygood.api.report.dto.ReportRequest;
+import org.wooriverygood.api.report.repository.PostReportRepository;
+
+@Service
+@Transactional
+@RequiredArgsConstructor
+public class PostReportService {
+
+ private final PostRepository postRepository;
+
+ private final PostReportRepository postReportRepository;
+
+ private final MemberRepository memberRepository;
+
+
+ public void reportPost(Long postId, ReportRequest request, AuthInfo authInfo) {
+ Member member = memberRepository.findById(authInfo.getMemberId())
+ .orElseThrow(MemberNotFoundException::new);
+ Post post = postRepository.findById(postId)
+ .orElseThrow(PostNotFoundException::new);
+
+ PostReport report = PostReport.builder()
+ .post(post)
+ .message(request.getMessage())
+ .member(member)
+ .build();
+
+ checkIfAlreadyReport(post, member);
+ post.addReport(report);
+ postRepository.increaseReportCount(postId);
+
+ postReportRepository.save(report);
+ }
+
+ private void checkIfAlreadyReport(Post post, Member member) {
+ if (post.hasReportByMember(member))
+ throw new DuplicatedPostReportException();
+ }
+
+}
diff --git a/src/main/java/org/wooriverygood/api/report/domain/CommentReport.java b/src/main/java/org/wooriverygood/api/report/domain/CommentReport.java
index 336b0a6..917a59f 100644
--- a/src/main/java/org/wooriverygood/api/report/domain/CommentReport.java
+++ b/src/main/java/org/wooriverygood/api/report/domain/CommentReport.java
@@ -1,14 +1,17 @@
package org.wooriverygood.api.report.domain;
import jakarta.persistence.*;
+import lombok.AccessLevel;
import lombok.Builder;
import lombok.Getter;
import lombok.NoArgsConstructor;
import org.wooriverygood.api.comment.domain.Comment;
+import org.wooriverygood.api.member.domain.Member;
@Entity
@Getter
-@NoArgsConstructor
+@Table(name = "comment_reports")
+@NoArgsConstructor(access = AccessLevel.PROTECTED)
public class CommentReport {
@Id
@@ -19,21 +22,21 @@ public class CommentReport {
@JoinColumn(name = "comment_id", referencedColumnName = "comment_id")
private Comment comment;
- @Column
- private String username;
+ @ManyToOne(fetch = FetchType.LAZY)
+ private Member member;
private String message;
@Builder
- public CommentReport(Long id, Comment comment, String username, String message) {
+ public CommentReport(Long id, Comment comment, Member member, String message) {
this.id = id;
this.comment = comment;
- this.username = username;
+ this.member = member;
this.message = message;
}
- public boolean isOwner(String username) {
- return this.username.equals(username);
+ public boolean isOwner(Member member) {
+ return this.member.equals(member);
}
}
diff --git a/src/main/java/org/wooriverygood/api/report/domain/PostReport.java b/src/main/java/org/wooriverygood/api/report/domain/PostReport.java
index 77197b9..e34ae25 100644
--- a/src/main/java/org/wooriverygood/api/report/domain/PostReport.java
+++ b/src/main/java/org/wooriverygood/api/report/domain/PostReport.java
@@ -1,14 +1,17 @@
package org.wooriverygood.api.report.domain;
import jakarta.persistence.*;
+import lombok.AccessLevel;
import lombok.Builder;
import lombok.Getter;
import lombok.NoArgsConstructor;
+import org.wooriverygood.api.member.domain.Member;
import org.wooriverygood.api.post.domain.Post;
@Entity
@Getter
-@NoArgsConstructor
+@Table(name = "post_reports")
+@NoArgsConstructor(access = AccessLevel.PROTECTED)
public class PostReport {
@Id
@@ -19,22 +22,22 @@ public class PostReport {
@JoinColumn(name = "post_id", referencedColumnName = "post_id")
private Post post;
- @Column
- private String username;
+ @ManyToOne(fetch = FetchType.LAZY)
+ private Member member;
private String message;
@Builder
- public PostReport(Long id, Post post, String username, String message) {
+ public PostReport(Long id, Post post, Member member, String message) {
this.id = id;
this.post = post;
- this.username = username;
+ this.member = member;
this.message = message;
}
- public boolean isOwner(String username) {
- return this.username.equals(username);
+ public boolean isOwner(Member member) {
+ return this.member.equals(member);
}
}
diff --git a/src/main/java/org/wooriverygood/api/report/dto/ReportRequest.java b/src/main/java/org/wooriverygood/api/report/dto/ReportRequest.java
index ec447b2..b95509a 100644
--- a/src/main/java/org/wooriverygood/api/report/dto/ReportRequest.java
+++ b/src/main/java/org/wooriverygood/api/report/dto/ReportRequest.java
@@ -1,7 +1,6 @@
package org.wooriverygood.api.report.dto;
import jakarta.validation.constraints.NotBlank;
-import lombok.Builder;
import lombok.Getter;
import lombok.NoArgsConstructor;
@@ -12,8 +11,8 @@ public class ReportRequest {
@NotBlank(message = "신고내용은 1자 이상 255자 이하여야 합니다.")
private String message;
- @Builder
public ReportRequest(String message) {
this.message = message;
}
+
}
diff --git a/src/main/java/org/wooriverygood/api/advice/exception/DuplicatedCommentReportException.java b/src/main/java/org/wooriverygood/api/report/exception/DuplicatedCommentReportException.java
similarity index 66%
rename from src/main/java/org/wooriverygood/api/advice/exception/DuplicatedCommentReportException.java
rename to src/main/java/org/wooriverygood/api/report/exception/DuplicatedCommentReportException.java
index 8d29d16..bcd3acc 100644
--- a/src/main/java/org/wooriverygood/api/advice/exception/DuplicatedCommentReportException.java
+++ b/src/main/java/org/wooriverygood/api/report/exception/DuplicatedCommentReportException.java
@@ -1,6 +1,6 @@
-package org.wooriverygood.api.advice.exception;
+package org.wooriverygood.api.report.exception;
-import org.wooriverygood.api.advice.exception.general.BadRequestException;
+import org.wooriverygood.api.global.error.exception.BadRequestException;
public class DuplicatedCommentReportException extends BadRequestException {
diff --git a/src/main/java/org/wooriverygood/api/advice/exception/DuplicatedPostReportException.java b/src/main/java/org/wooriverygood/api/report/exception/DuplicatedPostReportException.java
similarity index 65%
rename from src/main/java/org/wooriverygood/api/advice/exception/DuplicatedPostReportException.java
rename to src/main/java/org/wooriverygood/api/report/exception/DuplicatedPostReportException.java
index 074091c..3ec45d8 100644
--- a/src/main/java/org/wooriverygood/api/advice/exception/DuplicatedPostReportException.java
+++ b/src/main/java/org/wooriverygood/api/report/exception/DuplicatedPostReportException.java
@@ -1,6 +1,6 @@
-package org.wooriverygood.api.advice.exception;
+package org.wooriverygood.api.report.exception;
-import org.wooriverygood.api.advice.exception.general.BadRequestException;
+import org.wooriverygood.api.global.error.exception.BadRequestException;
public class DuplicatedPostReportException extends BadRequestException {
diff --git a/src/main/java/org/wooriverygood/api/report/service/ReportService.java b/src/main/java/org/wooriverygood/api/report/service/ReportService.java
deleted file mode 100644
index 8757453..0000000
--- a/src/main/java/org/wooriverygood/api/report/service/ReportService.java
+++ /dev/null
@@ -1,83 +0,0 @@
-package org.wooriverygood.api.report.service;
-
-import org.springframework.stereotype.Service;
-import org.springframework.transaction.annotation.Transactional;
-import org.wooriverygood.api.advice.exception.CommentNotFoundException;
-import org.wooriverygood.api.advice.exception.DuplicatedCommentReportException;
-import org.wooriverygood.api.advice.exception.DuplicatedPostReportException;
-import org.wooriverygood.api.advice.exception.PostNotFoundException;
-import org.wooriverygood.api.comment.domain.Comment;
-import org.wooriverygood.api.comment.repository.CommentRepository;
-import org.wooriverygood.api.post.domain.Post;
-import org.wooriverygood.api.post.repository.PostRepository;
-import org.wooriverygood.api.report.domain.CommentReport;
-import org.wooriverygood.api.report.domain.PostReport;
-import org.wooriverygood.api.report.dto.ReportRequest;
-import org.wooriverygood.api.report.repository.CommentReportRepository;
-import org.wooriverygood.api.report.repository.PostReportRepository;
-import org.wooriverygood.api.support.AuthInfo;
-
-@Service
-@Transactional
-public class ReportService {
-
- private final CommentRepository commentRepository;
-
- private final CommentReportRepository commentReportRepository;
-
- private final PostRepository postRepository;
-
- private final PostReportRepository postReportRepository;
-
-
- public ReportService(CommentRepository commentRepository, CommentReportRepository commentReportRepository, PostRepository postRepository, PostReportRepository postReportRepository) {
- this.commentRepository = commentRepository;
- this.commentReportRepository = commentReportRepository;
- this.postRepository = postRepository;
- this.postReportRepository = postReportRepository;
- }
-
- public void reportPost(Long postId, ReportRequest request, AuthInfo authInfo) {
- Post post = postRepository.findById(postId)
- .orElseThrow(PostNotFoundException::new);
-
- PostReport report = PostReport.builder()
- .post(post)
- .message(request.getMessage())
- .username(authInfo.getUsername())
- .build();
-
- checkAlreadyReport(post, authInfo);
- post.addReport(report);
- postRepository.increaseReportCount(postId);
-
- postReportRepository.save(report);
- }
-
- private void checkAlreadyReport(Post post, AuthInfo authInfo) {
- if (post.hasReportByUser(authInfo.getUsername()))
- throw new DuplicatedPostReportException();
- }
-
- public void reportComment(Long commentId, ReportRequest request, AuthInfo authInfo) {
- Comment comment = commentRepository.findById(commentId)
- .orElseThrow(CommentNotFoundException::new);
-
- CommentReport report = CommentReport.builder()
- .comment(comment)
- .message(request.getMessage())
- .username(authInfo.getUsername())
- .build();
-
- checkAlreadyReport(comment, authInfo);
- comment.addReport(report);
- commentRepository.increaseReportCount(commentId);
-
- commentReportRepository.save(report);
- }
-
- private void checkAlreadyReport(Comment comment, AuthInfo authInfo) {
- if (comment.hasReportByUser(authInfo.getUsername()))
- throw new DuplicatedCommentReportException();
- }
-}
diff --git a/src/main/java/org/wooriverygood/api/review/api/ReviewApi.java b/src/main/java/org/wooriverygood/api/review/api/ReviewApi.java
new file mode 100644
index 0000000..0fbdd99
--- /dev/null
+++ b/src/main/java/org/wooriverygood/api/review/api/ReviewApi.java
@@ -0,0 +1,73 @@
+package org.wooriverygood.api.review.api;
+
+import jakarta.validation.Valid;
+import lombok.RequiredArgsConstructor;
+import org.springframework.http.HttpStatus;
+import org.springframework.http.ResponseEntity;
+import org.springframework.web.bind.annotation.*;
+import org.wooriverygood.api.review.application.*;
+import org.wooriverygood.api.review.dto.*;
+import org.wooriverygood.api.global.auth.AuthInfo;
+import org.wooriverygood.api.global.auth.Login;
+
+@RestController
+@RequiredArgsConstructor
+public class ReviewApi {
+
+ private final ReviewLikeToggleService reviewLikeToggleService;
+
+ private final ReviewCreateService reviewCreateService;
+
+ private final ReviewDeleteService reviewDeleteService;
+
+ private final ReviewFindService reviewFindService;
+
+ private final ReviewUpdateService reviewUpdateService;
+
+ private final ReviewValidateAccessService reviewValidateAccessService;
+
+
+ @GetMapping("/courses/{id}/reviews")
+ public ResponseEntity findAllReviewsByCourseId(@PathVariable("id") Long courseId,
+ @Login AuthInfo authInfo) {
+ reviewValidateAccessService.validateReviewAccess(authInfo);
+ ReviewsResponse response = reviewFindService.findAllReviewsByCourseId(courseId, authInfo);
+ return ResponseEntity.ok(response);
+ }
+
+ @PostMapping("/courses/{id}/reviews")
+ public ResponseEntity addReview(@PathVariable("id") Long courseId, @Login AuthInfo authInfo,
+ @Valid @RequestBody NewReviewRequest request) {
+ reviewCreateService.addReview(authInfo, courseId, request);
+ return ResponseEntity.status(HttpStatus.CREATED).build();
+ }
+
+ @PutMapping("/reviews/{rid}/like")
+ public ResponseEntity likeReview(@PathVariable("rid") Long reviewId,
+ @Login AuthInfo authInfo) {
+ ReviewLikeResponse response= reviewLikeToggleService.toggleReviewLike(reviewId, authInfo);
+ return ResponseEntity.ok(response);
+ }
+
+ @GetMapping("/reviews/me")
+ public ResponseEntity findMyReviews(@Login AuthInfo authInfo) {
+ ReviewsResponse response = reviewFindService.findMyReviews(authInfo);
+ return ResponseEntity.ok(response);
+ }
+
+ @PutMapping("/reviews/{id}")
+ public ResponseEntity updateReview(@PathVariable("id") Long reviewId,
+ @Valid @RequestBody ReviewUpdateRequest request,
+ @Login AuthInfo authInfo) {
+ reviewUpdateService.updateReview(reviewId, request, authInfo);
+ return ResponseEntity.noContent().build();
+ }
+
+ @DeleteMapping("/reviews/{id}")
+ public ResponseEntity deleteReview(@PathVariable("id") Long reviewId,
+ @Login AuthInfo authInfo) {
+ reviewDeleteService.deleteReview(reviewId, authInfo);
+ return ResponseEntity.noContent().build();
+ }
+
+}
diff --git a/src/main/java/org/wooriverygood/api/review/application/ReviewCreateService.java b/src/main/java/org/wooriverygood/api/review/application/ReviewCreateService.java
new file mode 100644
index 0000000..b059183
--- /dev/null
+++ b/src/main/java/org/wooriverygood/api/review/application/ReviewCreateService.java
@@ -0,0 +1,53 @@
+package org.wooriverygood.api.review.application;
+
+import lombok.RequiredArgsConstructor;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+import org.wooriverygood.api.course.exception.CourseNotFoundException;
+import org.wooriverygood.api.course.domain.Course;
+import org.wooriverygood.api.course.repository.CourseRepository;
+import org.wooriverygood.api.global.auth.AuthInfo;
+import org.wooriverygood.api.member.domain.Member;
+import org.wooriverygood.api.member.exception.MemberNotFoundException;
+import org.wooriverygood.api.member.repository.MemberRepository;
+import org.wooriverygood.api.review.domain.Review;
+import org.wooriverygood.api.review.dto.NewReviewRequest;
+import org.wooriverygood.api.review.repository.ReviewRepository;
+
+@Service
+@RequiredArgsConstructor
+public class ReviewCreateService {
+
+ private final CourseRepository courseRepository;
+
+ private final ReviewRepository reviewRepository;
+
+ private final MemberRepository memberRepository;
+
+
+ @Transactional
+ public void addReview(AuthInfo authInfo, Long courseId, NewReviewRequest request) {
+ Course course = courseRepository.findById(courseId)
+ .orElseThrow(CourseNotFoundException::new);
+ Member member = memberRepository.findById(authInfo.getMemberId())
+ .orElseThrow(MemberNotFoundException::new);
+ Review review = createReview(course, request, member);
+ member.addReview(review);
+
+ reviewRepository.save(review);
+ courseRepository.increaseReviewCount(review.getCourse().getId());
+ }
+
+ private Review createReview(Course course, NewReviewRequest request, Member member) {
+ return Review.builder()
+ .reviewTitle(request.getReviewTitle())
+ .course(course)
+ .instructorName(request.getInstructorName())
+ .member(member)
+ .takenSemyr(request.getTakenSemyr())
+ .reviewContent(request.getReviewContent())
+ .grade(request.getGrade())
+ .build();
+ }
+
+}
diff --git a/src/main/java/org/wooriverygood/api/review/application/ReviewDeleteService.java b/src/main/java/org/wooriverygood/api/review/application/ReviewDeleteService.java
new file mode 100644
index 0000000..8e1f122
--- /dev/null
+++ b/src/main/java/org/wooriverygood/api/review/application/ReviewDeleteService.java
@@ -0,0 +1,42 @@
+package org.wooriverygood.api.review.application;
+
+import lombok.RequiredArgsConstructor;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+import org.wooriverygood.api.course.repository.CourseRepository;
+import org.wooriverygood.api.global.auth.AuthInfo;
+import org.wooriverygood.api.member.domain.Member;
+import org.wooriverygood.api.member.exception.MemberNotFoundException;
+import org.wooriverygood.api.member.repository.MemberRepository;
+import org.wooriverygood.api.review.domain.Review;
+import org.wooriverygood.api.review.exception.ReviewNotFoundException;
+import org.wooriverygood.api.review.repository.ReviewLikeRepository;
+import org.wooriverygood.api.review.repository.ReviewRepository;
+
+@Service
+@RequiredArgsConstructor
+public class ReviewDeleteService {
+
+ private final CourseRepository courseRepository;
+
+ private final ReviewRepository reviewRepository;
+
+ private final ReviewLikeRepository reviewLikeRepository;
+
+ private final MemberRepository memberRepository;
+
+
+ @Transactional
+ public void deleteReview(Long reviewId, AuthInfo authInfo) {
+ Review review = reviewRepository.findById(reviewId)
+ .orElseThrow(ReviewNotFoundException::new);
+ Member member = memberRepository.findById(authInfo.getMemberId())
+ .orElseThrow(MemberNotFoundException::new);
+ review.validateAuthor(member);
+
+ reviewLikeRepository.deleteAllByReview(review);
+ reviewRepository.delete(review);
+ courseRepository.decreaseReviewCount(review.getCourse().getId());
+ }
+
+}
diff --git a/src/main/java/org/wooriverygood/api/review/application/ReviewFindService.java b/src/main/java/org/wooriverygood/api/review/application/ReviewFindService.java
new file mode 100644
index 0000000..3e51951
--- /dev/null
+++ b/src/main/java/org/wooriverygood/api/review/application/ReviewFindService.java
@@ -0,0 +1,57 @@
+package org.wooriverygood.api.review.application;
+
+import lombok.RequiredArgsConstructor;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+import org.wooriverygood.api.global.auth.AuthInfo;
+import org.wooriverygood.api.member.domain.Member;
+import org.wooriverygood.api.member.exception.MemberNotFoundException;
+import org.wooriverygood.api.member.repository.MemberRepository;
+import org.wooriverygood.api.review.domain.Review;
+import org.wooriverygood.api.review.dto.ReviewResponse;
+import org.wooriverygood.api.review.dto.ReviewsResponse;
+import org.wooriverygood.api.review.repository.ReviewLikeRepository;
+import org.wooriverygood.api.review.repository.ReviewRepository;
+
+import java.util.List;
+
+@Service
+@RequiredArgsConstructor
+@Transactional(readOnly = true)
+public class ReviewFindService {
+
+ private final ReviewRepository reviewRepository;
+
+ private final ReviewLikeRepository reviewLikeRepository;
+
+ private final MemberRepository memberRepository;
+
+
+ public ReviewsResponse findAllReviewsByCourseId(Long courseId, AuthInfo authInfo) {
+ List reviews = reviewRepository.findAllByCourseId(courseId);
+ Member member = memberRepository.findById(authInfo.getMemberId())
+ .orElseThrow(MemberNotFoundException::new);
+
+ return new ReviewsResponse(reviews.stream()
+ .map(review -> {
+ boolean liked = reviewLikeRepository.existsByReviewAndMember(review, member);
+ return ReviewResponse.of(review,
+ review.isSameAuthor(member),
+ liked);
+ })
+ .toList());
+ }
+
+ public ReviewsResponse findMyReviews(AuthInfo authInfo) {
+ Member member = memberRepository.findById(authInfo.getMemberId())
+ .orElseThrow(MemberNotFoundException::new);
+ List reviews= member.getReviews();
+ return new ReviewsResponse(reviews.stream()
+ .map(review -> {
+ boolean liked = reviewLikeRepository.existsByReviewAndMember(review, member);
+ return ReviewResponse.of(review, true, liked);
+ })
+ .toList());
+ }
+
+}
diff --git a/src/main/java/org/wooriverygood/api/review/application/ReviewLikeToggleService.java b/src/main/java/org/wooriverygood/api/review/application/ReviewLikeToggleService.java
new file mode 100644
index 0000000..44925c0
--- /dev/null
+++ b/src/main/java/org/wooriverygood/api/review/application/ReviewLikeToggleService.java
@@ -0,0 +1,73 @@
+package org.wooriverygood.api.review.application;
+
+import lombok.RequiredArgsConstructor;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+import org.wooriverygood.api.member.domain.Member;
+import org.wooriverygood.api.member.exception.MemberNotFoundException;
+import org.wooriverygood.api.member.repository.MemberRepository;
+import org.wooriverygood.api.review.exception.ReviewNotFoundException;
+import org.wooriverygood.api.review.domain.Review;
+import org.wooriverygood.api.review.domain.ReviewLike;
+import org.wooriverygood.api.review.dto.*;
+import org.wooriverygood.api.review.repository.ReviewLikeRepository;
+import org.wooriverygood.api.review.repository.ReviewRepository;
+import org.wooriverygood.api.global.auth.AuthInfo;
+
+import java.util.Optional;
+
+@Service
+@Transactional
+@RequiredArgsConstructor
+public class ReviewLikeToggleService {
+
+ private final ReviewRepository reviewRepository;
+
+ private final ReviewLikeRepository reviewLikeRepository;
+
+ private final MemberRepository memberRepository;
+
+
+ public ReviewLikeResponse toggleReviewLike(Long reviewId, AuthInfo authInfo) {
+ Review review = reviewRepository.findById(reviewId)
+ .orElseThrow(ReviewNotFoundException::new);
+ Member member = memberRepository.findById(authInfo.getMemberId())
+ .orElseThrow(MemberNotFoundException::new);
+
+ Optional reviewLike = reviewLikeRepository.findByReviewAndMember(review, member);
+
+ if (reviewLike.isEmpty()) {
+ addReviewLike(review, member);
+ return createReviewLikeResponse(review, true);
+ }
+
+ deleteReviewLike(review, reviewLike.get());
+ return createReviewLikeResponse(review, false);
+
+ }
+
+ private void addReviewLike(Review review, Member member) {
+ ReviewLike reviewLike = ReviewLike.builder()
+ .review(review)
+ .member(member)
+ .build();
+
+ review.addReviewLike(reviewLike);
+ reviewLikeRepository.save(reviewLike);
+ reviewRepository.increaseLikeCount(review.getId());
+ }
+
+ private void deleteReviewLike(Review review, ReviewLike reviewLike) {
+ review.deleteReviewLike(reviewLike);
+ reviewRepository.decreaseLikeCount(review.getId());
+ }
+
+ private ReviewLikeResponse createReviewLikeResponse(Review review, boolean liked) {
+ int likeCount = review.getLikeCount() + (liked ? 1 : -1);
+ return ReviewLikeResponse.builder()
+ .likeCount(likeCount)
+ .liked(liked)
+ .build();
+ }
+
+}
diff --git a/src/main/java/org/wooriverygood/api/review/application/ReviewUpdateService.java b/src/main/java/org/wooriverygood/api/review/application/ReviewUpdateService.java
new file mode 100644
index 0000000..d838810
--- /dev/null
+++ b/src/main/java/org/wooriverygood/api/review/application/ReviewUpdateService.java
@@ -0,0 +1,39 @@
+package org.wooriverygood.api.review.application;
+
+import lombok.RequiredArgsConstructor;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+import org.wooriverygood.api.global.auth.AuthInfo;
+import org.wooriverygood.api.member.domain.Member;
+import org.wooriverygood.api.member.exception.MemberNotFoundException;
+import org.wooriverygood.api.member.repository.MemberRepository;
+import org.wooriverygood.api.review.domain.Review;
+import org.wooriverygood.api.review.dto.ReviewUpdateRequest;
+import org.wooriverygood.api.review.exception.ReviewNotFoundException;
+import org.wooriverygood.api.review.repository.ReviewRepository;
+
+@Service
+@RequiredArgsConstructor
+public class ReviewUpdateService {
+
+ private final ReviewRepository reviewRepository;
+
+ private final MemberRepository memberRepository;
+
+
+ @Transactional
+ public void updateReview(Long reviewId, ReviewUpdateRequest request, AuthInfo authInfo) {
+ Review review = reviewRepository.findById(reviewId)
+ .orElseThrow(ReviewNotFoundException::new);
+ Member member = memberRepository.findById(authInfo.getMemberId())
+ .orElseThrow(MemberNotFoundException::new);
+ review.validateAuthor(member);
+
+ review.updateTitle(request.getReviewTitle());
+ review.updateInstructorName(request.getInstructorName());
+ review.updateTakenSemyr(request.getTakenSemyr());
+ review.updateContent(request.getReviewContent());
+ review.updateGrade(request.getGrade());
+ }
+
+}
diff --git a/src/main/java/org/wooriverygood/api/review/application/ReviewValidateAccessService.java b/src/main/java/org/wooriverygood/api/review/application/ReviewValidateAccessService.java
new file mode 100644
index 0000000..625f790
--- /dev/null
+++ b/src/main/java/org/wooriverygood/api/review/application/ReviewValidateAccessService.java
@@ -0,0 +1,39 @@
+package org.wooriverygood.api.review.application;
+
+import lombok.RequiredArgsConstructor;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+import org.wooriverygood.api.member.domain.Member;
+import org.wooriverygood.api.member.exception.MemberNotFoundException;
+import org.wooriverygood.api.member.repository.MemberRepository;
+import org.wooriverygood.api.review.exception.ReviewAccessDeniedException;
+import org.wooriverygood.api.global.auth.AuthInfo;
+import org.wooriverygood.api.review.domain.Review;
+import org.wooriverygood.api.review.repository.ReviewRepository;
+
+import java.time.LocalDateTime;
+import java.time.temporal.ChronoUnit;
+
+@Service
+@Transactional(readOnly = true)
+@RequiredArgsConstructor
+public class ReviewValidateAccessService {
+
+ private final ReviewRepository reviewRepository;
+
+ private final MemberRepository memberRepository;
+
+
+ public void validateReviewAccess(AuthInfo authInfo) {
+ Member member = memberRepository.findById(authInfo.getMemberId())
+ .orElseThrow(MemberNotFoundException::new);
+ Review review = reviewRepository.findTopByMemberOrderByCreatedAtDesc(member)
+ .orElseThrow(ReviewAccessDeniedException::new);
+
+ LocalDateTime now = LocalDateTime.now();
+ long distance = ChronoUnit.MONTHS.between(review.getCreatedAt(), now);
+ if (distance > 6)
+ throw new ReviewAccessDeniedException();
+ }
+
+}
diff --git a/src/main/java/org/wooriverygood/api/review/controller/ReviewController.java b/src/main/java/org/wooriverygood/api/review/controller/ReviewController.java
deleted file mode 100644
index 173143e..0000000
--- a/src/main/java/org/wooriverygood/api/review/controller/ReviewController.java
+++ /dev/null
@@ -1,71 +0,0 @@
-package org.wooriverygood.api.review.controller;
-
-import jakarta.validation.Valid;
-import org.springframework.http.HttpStatus;
-import org.springframework.http.ResponseEntity;
-import org.springframework.web.bind.annotation.*;
-import org.wooriverygood.api.advice.exception.ReviewAccessDeniedException;
-import org.wooriverygood.api.review.dto.*;
-import org.wooriverygood.api.review.service.ReviewService;
-import org.wooriverygood.api.support.AuthInfo;
-import org.wooriverygood.api.support.Login;
-
-import java.util.List;
-
-@RestController
-public class ReviewController {
- private final ReviewService reviewService;
-
- public ReviewController(ReviewService reviewService) {
- this.reviewService = reviewService;
- }
-
- @GetMapping("/")
- public ResponseEntity apiOnline() {
- return ResponseEntity.ok("API Online!");
- }
-
- @GetMapping("/courses/{id}/reviews")
- public ResponseEntity> findAllReviewsByCourseId(@PathVariable("id") Long courseId, @Login AuthInfo authInfo) {
- if (!reviewService.canAccessReviews(authInfo)) {
- throw new ReviewAccessDeniedException();
- }
- List reviews = reviewService.findAllReviewsByCourseId(courseId, authInfo);
- return ResponseEntity.ok(reviews);
- }
-
- @PostMapping("/courses/{id}/reviews")
- public ResponseEntity addReview(@PathVariable("id") Long courseId, @Login AuthInfo authInfo, @Valid @RequestBody NewReviewRequest newReviewRequest) {
- NewReviewResponse response = reviewService.addReview(authInfo, courseId, newReviewRequest);
- return ResponseEntity.status(HttpStatus.CREATED).body(response);
- }
-
- @PutMapping("/courses/reviews/{rid}/like")
- public ResponseEntity likeReview(@PathVariable("rid") Long reviewId, @Login AuthInfo authInfo) {
- ReviewLikeResponse response= reviewService.likeReview(reviewId, authInfo);
- return ResponseEntity.ok(response);
- }
-
- @GetMapping("/courses/reviews/me")
- public ResponseEntity> findMyPosts(@Login AuthInfo authInfo) {
- List reviewResponses = reviewService.findMyReviews(authInfo);
- return ResponseEntity.ok(reviewResponses);
- }
-
- @PutMapping("/courses/reviews/{rid}")
- public ResponseEntity updateReview(@PathVariable("rid") Long reviewId,
- @Valid @RequestBody ReviewUpdateRequest reviewUpdateRequest,
- @Login AuthInfo authInfo) {
- ReviewUpdateResponse response = reviewService.updateReview(reviewId, reviewUpdateRequest, authInfo);
- return ResponseEntity.ok(response);
- }
-
- @DeleteMapping("/courses/reviews/{rid}")
- public ResponseEntity deleteReview(@PathVariable("rid") Long reviewId,
- @Login AuthInfo authInfo) {
- ReviewDeleteResponse response = reviewService.deleteReview(reviewId, authInfo);
- return ResponseEntity.ok(response);
- }
-
-
-}
diff --git a/src/main/java/org/wooriverygood/api/review/domain/Review.java b/src/main/java/org/wooriverygood/api/review/domain/Review.java
index ee41882..2e339a0 100644
--- a/src/main/java/org/wooriverygood/api/review/domain/Review.java
+++ b/src/main/java/org/wooriverygood/api/review/domain/Review.java
@@ -1,23 +1,25 @@
package org.wooriverygood.api.review.domain;
import jakarta.persistence.*;
+import lombok.AccessLevel;
import lombok.Builder;
import lombok.Getter;
import lombok.NoArgsConstructor;
import org.hibernate.annotations.ColumnDefault;
import org.springframework.data.annotation.CreatedDate;
import org.springframework.data.jpa.domain.support.AuditingEntityListener;
-import org.wooriverygood.api.advice.exception.AuthorizationException;
-import org.wooriverygood.api.course.domain.Courses;
+import org.wooriverygood.api.global.error.exception.AuthorizationException;
+import org.wooriverygood.api.course.domain.Course;
+import org.wooriverygood.api.member.domain.Member;
import java.time.LocalDateTime;
import java.util.List;
@Entity
@Getter
-@NoArgsConstructor
-@EntityListeners(AuditingEntityListener.class)
@Table(name = "reviews")
+@EntityListeners(AuditingEntityListener.class)
+@NoArgsConstructor(access = AccessLevel.PROTECTED)
public class Review {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@@ -26,7 +28,7 @@ public class Review {
@ManyToOne
@JoinColumn(name = "course_id", referencedColumnName = "course_id")
- private Courses course;
+ private Course course;
@Column(name = "review_content", length = 1000)
private String reviewContent;
@@ -43,8 +45,8 @@ public class Review {
@Column(name = "grade", length = 45, nullable = false)
private String grade;
- @Column(name = "author_email", length = 300)
- private String authorEmail;
+ @ManyToOne(fetch = FetchType.LAZY)
+ private Member member;
@Column(name = "like_count")
@ColumnDefault("0")
@@ -61,7 +63,9 @@ public class Review {
private boolean updated;
@Builder
- public Review(Long id, Courses course, String reviewContent, String reviewTitle, String instructorName, String takenSemyr, String grade, String authorEmail, LocalDateTime createdAt, List reviewLikes, boolean updated) {
+ public Review(Long id, Course course, String reviewContent, String reviewTitle, String instructorName,
+ String takenSemyr, String grade, LocalDateTime createdAt, Member member,
+ List reviewLikes, boolean updated) {
this.id = id;
this.course = course;
this.reviewContent = reviewContent;
@@ -69,20 +73,18 @@ public Review(Long id, Courses course, String reviewContent, String reviewTitle,
this.instructorName = instructorName;
this.takenSemyr = takenSemyr;
this.grade = grade;
- this.authorEmail = authorEmail;
this.createdAt = createdAt;
this.reviewLikes = reviewLikes;
this.updated = updated;
+ this.member = member;
}
- public boolean isSameAuthor(String author) {
- return this.authorEmail.equals(author);
+ public boolean isSameAuthor(Member member) {
+ return this.member.isSame(member);
}
- public void validateAuthor(String author) {
- if (!this.authorEmail.equals(author)) {
- throw new AuthorizationException();
- }
+ public void validateAuthor(Member member) {
+ this.member.verify(member);
}
public void addReviewLike(ReviewLike reviewLike) {
@@ -94,19 +96,29 @@ public void deleteReviewLike(ReviewLike reviewLike) {
reviewLike.delete();
}
- public void updateReview(String title, String instructorName, String takenSemyr, String content, String grade, String author) {
- this.reviewTitle = title;
- this.reviewContent = content;
- this.instructorName = instructorName;
+ public void updateTitle(String title) {
+ reviewTitle = title;
+ updated = true;
+ }
+
+ public void updateContent(String content) {
+ reviewContent = content;
+ updated = true;
+ }
+
+ public void updateTakenSemyr(String takenSemyr) {
this.takenSemyr = takenSemyr;
+ updated = true;
+ }
+
+ public void updateInstructorName(String instructorName) {
+ this.instructorName = instructorName;
+ updated = true;
+ }
+
+ public void updateGrade(String grade) {
this.grade = grade;
updated = true;
- if (id <= 234) {
- authorEmail = author;
- }
- if (createdAt == null) {
- createdAt = LocalDateTime.now();
- }
}
}
diff --git a/src/main/java/org/wooriverygood/api/review/domain/ReviewLike.java b/src/main/java/org/wooriverygood/api/review/domain/ReviewLike.java
index 96e0960..bbf5398 100644
--- a/src/main/java/org/wooriverygood/api/review/domain/ReviewLike.java
+++ b/src/main/java/org/wooriverygood/api/review/domain/ReviewLike.java
@@ -1,30 +1,34 @@
package org.wooriverygood.api.review.domain;
import jakarta.persistence.*;
+import lombok.AccessLevel;
import lombok.Builder;
import lombok.Getter;
import lombok.NoArgsConstructor;
+import org.wooriverygood.api.member.domain.Member;
@Entity
-@Table(name = "reviewLikes")
@Getter
-@NoArgsConstructor
+@Table(name = "review_likes")
+@NoArgsConstructor(access = AccessLevel.PROTECTED)
public class ReviewLike {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
- @ManyToOne
+ @ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "review_id", referencedColumnName = "review_id")
private Review review;
- @Column
- private String username;
+ @ManyToOne(fetch = FetchType.LAZY)
+ private Member member;
+
+
@Builder
- public ReviewLike(Long id, Review review, String username) {
+ public ReviewLike(Long id, Review review, Member member) {
this.id = id;
this.review = review;
- this.username = username;
+ this.member = member;
}
public void delete() { review = null; }
diff --git a/src/main/java/org/wooriverygood/api/review/dto/NewReviewRequest.java b/src/main/java/org/wooriverygood/api/review/dto/NewReviewRequest.java
index 9069bbf..c036373 100644
--- a/src/main/java/org/wooriverygood/api/review/dto/NewReviewRequest.java
+++ b/src/main/java/org/wooriverygood/api/review/dto/NewReviewRequest.java
@@ -1,22 +1,35 @@
package org.wooriverygood.api.review.dto;
+import com.fasterxml.jackson.databind.PropertyNamingStrategies;
+import com.fasterxml.jackson.databind.annotation.JsonNaming;
+import jakarta.validation.constraints.NotBlank;
import lombok.Builder;
import lombok.Getter;
@Getter
+@JsonNaming(PropertyNamingStrategies.SnakeCaseStrategy.class)
public class NewReviewRequest {
- private final String review_title;
- private final String instructor_name;
- private final String taken_semyr;
- private final String review_content;
+
+ @NotBlank(message = "제목이 없습니다.")
+ private final String reviewTitle;
+
+ private final String instructorName;
+
+ private final String takenSemyr;
+
+ private final String reviewContent;
+
private final String grade;
+
@Builder
- public NewReviewRequest(String review_title, String instructor_name, String taken_semyr, String review_content, String grade) {
- this.review_title = review_title;
- this.instructor_name = instructor_name;
- this.taken_semyr = taken_semyr;
- this.review_content = review_content;
+ public NewReviewRequest(String reviewTitle, String instructorName, String takenSemyr,
+ String reviewContent, String grade) {
+ this.reviewTitle = reviewTitle;
+ this.instructorName = instructorName;
+ this.takenSemyr = takenSemyr;
+ this.reviewContent = reviewContent;
this.grade = grade;
}
+
}
diff --git a/src/main/java/org/wooriverygood/api/review/dto/NewReviewResponse.java b/src/main/java/org/wooriverygood/api/review/dto/NewReviewResponse.java
deleted file mode 100644
index 2ddc3f8..0000000
--- a/src/main/java/org/wooriverygood/api/review/dto/NewReviewResponse.java
+++ /dev/null
@@ -1,27 +0,0 @@
-package org.wooriverygood.api.review.dto;
-
-import lombok.Builder;
-import lombok.Getter;
-
-@Getter
-public class NewReviewResponse {
- private final Long review_id;
- private final String review_title;
- private final String instructor_name;
- private final String taken_semyr;
- private final String review_content;
- private final String grade;
- private final String author_email;
-
-
- @Builder
- public NewReviewResponse(Long review_id, String review_title, String instructor_name, String taken_semyr, String review_content, String grade, String author_email) {
- this.review_id = review_id;
- this.review_title = review_title;
- this.instructor_name = instructor_name;
- this.taken_semyr = taken_semyr;
- this.review_content = review_content;
- this.grade = grade;
- this.author_email = author_email;
- }
-}
diff --git a/src/main/java/org/wooriverygood/api/review/dto/ReviewDeleteResponse.java b/src/main/java/org/wooriverygood/api/review/dto/ReviewDeleteResponse.java
deleted file mode 100644
index db9ffe4..0000000
--- a/src/main/java/org/wooriverygood/api/review/dto/ReviewDeleteResponse.java
+++ /dev/null
@@ -1,14 +0,0 @@
-package org.wooriverygood.api.review.dto;
-
-import lombok.Builder;
-import lombok.Getter;
-
-@Getter
-public class ReviewDeleteResponse {
- private final Long review_id;
-
- @Builder
- public ReviewDeleteResponse(Long review_id) {
- this.review_id = review_id;
- }
-}
diff --git a/src/main/java/org/wooriverygood/api/review/dto/ReviewLikeResponse.java b/src/main/java/org/wooriverygood/api/review/dto/ReviewLikeResponse.java
index 644a8c4..9af6e66 100644
--- a/src/main/java/org/wooriverygood/api/review/dto/ReviewLikeResponse.java
+++ b/src/main/java/org/wooriverygood/api/review/dto/ReviewLikeResponse.java
@@ -1,16 +1,23 @@
package org.wooriverygood.api.review.dto;
+import com.fasterxml.jackson.databind.PropertyNamingStrategies;
+import com.fasterxml.jackson.databind.annotation.JsonNaming;
import lombok.Builder;
import lombok.Getter;
@Getter
+@JsonNaming(PropertyNamingStrategies.SnakeCaseStrategy.class)
public class ReviewLikeResponse {
- private final int like_count;
+
+ private final int likeCount;
+
private final boolean liked;
+
@Builder
- public ReviewLikeResponse(int like_count, boolean liked) {
- this.like_count = like_count;
+ public ReviewLikeResponse(int likeCount, boolean liked) {
+ this.likeCount = likeCount;
this.liked =liked;
}
+
}
diff --git a/src/main/java/org/wooriverygood/api/review/dto/ReviewResponse.java b/src/main/java/org/wooriverygood/api/review/dto/ReviewResponse.java
index 88cd695..c7dfd6e 100644
--- a/src/main/java/org/wooriverygood/api/review/dto/ReviewResponse.java
+++ b/src/main/java/org/wooriverygood/api/review/dto/ReviewResponse.java
@@ -1,5 +1,7 @@
package org.wooriverygood.api.review.dto;
+import com.fasterxml.jackson.databind.PropertyNamingStrategies;
+import com.fasterxml.jackson.databind.annotation.JsonNaming;
import lombok.Builder;
import lombok.Getter;
import org.wooriverygood.api.review.domain.Review;
@@ -7,47 +9,48 @@
import java.time.LocalDateTime;
@Getter
+@JsonNaming(PropertyNamingStrategies.SnakeCaseStrategy.class)
public class ReviewResponse {
- private final Long review_id;
- private final Long course_id;
- private final String review_content;
- private final String review_title;
- private final String instructor_name;
- private final String taken_semyr;
+ private final Long reviewId;
+ private final Long courseId;
+ private final String reviewContent;
+ private final String reviewTitle;
+ private final String instructorName;
+ private final String takenSemyr;
private final String grade;
- private final int like_count;
- private final LocalDateTime review_time;
+ private final int likeCount;
+ private final LocalDateTime reviewTime;
private final boolean isMine;
private final boolean liked;
private final boolean updated;
@Builder
- public ReviewResponse(Long review_id, Long course_id, String review_content, String review_title, String instructor_name, String taken_semyr, String grade, int like_count, LocalDateTime review_time, boolean isMine, boolean liked, boolean updated) {
- this.review_id = review_id;
- this.course_id = course_id;
- this.review_content = review_content;
- this.review_title = review_title;
- this.instructor_name = instructor_name;
- this.taken_semyr = taken_semyr;
+ public ReviewResponse(Long reviewId, Long courseId, String reviewContent, String reviewTitle, String instructorName, String takenSemyr, String grade, int likeCount, LocalDateTime reviewTime, boolean isMine, boolean liked, boolean updated) {
+ this.reviewId = reviewId;
+ this.courseId = courseId;
+ this.reviewContent = reviewContent;
+ this.reviewTitle = reviewTitle;
+ this.instructorName = instructorName;
+ this.takenSemyr = takenSemyr;
this.grade = grade;
- this.like_count = like_count;
- this.review_time = review_time;
+ this.likeCount = likeCount;
+ this.reviewTime = reviewTime;
this.isMine = isMine;
this.liked = liked;
this.updated = updated;
}
- public static ReviewResponse from(Review review, boolean isMine, boolean liked) {
+ public static ReviewResponse of(Review review, boolean isMine, boolean liked) {
return ReviewResponse.builder()
- .review_id(review.getId())
- .course_id(review.getCourse().getId())
- .review_content(review.getReviewContent())
- .review_title(review.getReviewTitle())
- .instructor_name(review.getInstructorName())
- .taken_semyr(review.getTakenSemyr())
+ .reviewId(review.getId())
+ .courseId(review.getCourse().getId())
+ .reviewContent(review.getReviewContent())
+ .reviewTitle(review.getReviewTitle())
+ .instructorName(review.getInstructorName())
+ .takenSemyr(review.getTakenSemyr())
.grade(review.getGrade())
- .like_count(review.getLikeCount())
- .review_time(review.getCreatedAt())
+ .likeCount(review.getLikeCount())
+ .reviewTime(review.getCreatedAt())
.isMine(isMine)
.liked(liked)
.updated(review.isUpdated())
diff --git a/src/main/java/org/wooriverygood/api/review/dto/ReviewUpdateRequest.java b/src/main/java/org/wooriverygood/api/review/dto/ReviewUpdateRequest.java
index 7590046..8d2de4a 100644
--- a/src/main/java/org/wooriverygood/api/review/dto/ReviewUpdateRequest.java
+++ b/src/main/java/org/wooriverygood/api/review/dto/ReviewUpdateRequest.java
@@ -1,26 +1,35 @@
package org.wooriverygood.api.review.dto;
+import com.fasterxml.jackson.databind.PropertyNamingStrategies;
+import com.fasterxml.jackson.databind.annotation.JsonNaming;
import jakarta.validation.constraints.NotBlank;
import lombok.Builder;
import lombok.Getter;
-import lombok.NoArgsConstructor;
@Getter
-@NoArgsConstructor
+@JsonNaming(PropertyNamingStrategies.SnakeCaseStrategy.class)
public class ReviewUpdateRequest {
+
@NotBlank(message = "제목이 없습니다.")
- private String review_title;
- private String instructor_name;
- private String taken_semyr;
- private String review_content;
+ private String reviewTitle;
+
+ private String instructorName;
+
+ private String takenSemyr;
+
+ private String reviewContent;
+
private String grade;
+
@Builder
- public ReviewUpdateRequest(String review_title, String instructor_name, String taken_semyr, String review_content, String grade) {
- this.review_title = review_title;
- this.instructor_name = instructor_name;
- this.taken_semyr = taken_semyr;
- this.review_content = review_content;
+ public ReviewUpdateRequest(String reviewTitle, String instructorName, String takenSemyr,
+ String reviewContent, String grade) {
+ this.reviewTitle = reviewTitle;
+ this.instructorName = instructorName;
+ this.takenSemyr = takenSemyr;
+ this.reviewContent = reviewContent;
this.grade = grade;
}
+
}
diff --git a/src/main/java/org/wooriverygood/api/review/dto/ReviewUpdateResponse.java b/src/main/java/org/wooriverygood/api/review/dto/ReviewUpdateResponse.java
deleted file mode 100644
index fb0399c..0000000
--- a/src/main/java/org/wooriverygood/api/review/dto/ReviewUpdateResponse.java
+++ /dev/null
@@ -1,15 +0,0 @@
-package org.wooriverygood.api.review.dto;
-
-import lombok.Builder;
-import lombok.Getter;
-
-@Getter
-public class ReviewUpdateResponse {
-
- private final Long review_id;
-
- @Builder
- public ReviewUpdateResponse(Long review_id) {
- this.review_id = review_id;
- }
-}
diff --git a/src/main/java/org/wooriverygood/api/review/dto/ReviewsResponse.java b/src/main/java/org/wooriverygood/api/review/dto/ReviewsResponse.java
new file mode 100644
index 0000000..fa04cae
--- /dev/null
+++ b/src/main/java/org/wooriverygood/api/review/dto/ReviewsResponse.java
@@ -0,0 +1,17 @@
+package org.wooriverygood.api.review.dto;
+
+import lombok.Getter;
+
+import java.util.List;
+
+@Getter
+public class ReviewsResponse {
+
+ private final List reviews;
+
+
+ public ReviewsResponse(List reviews) {
+ this.reviews = reviews;
+ }
+
+}
diff --git a/src/main/java/org/wooriverygood/api/advice/exception/ReviewAccessDeniedException.java b/src/main/java/org/wooriverygood/api/review/exception/ReviewAccessDeniedException.java
similarity index 67%
rename from src/main/java/org/wooriverygood/api/advice/exception/ReviewAccessDeniedException.java
rename to src/main/java/org/wooriverygood/api/review/exception/ReviewAccessDeniedException.java
index 9e09222..288c210 100644
--- a/src/main/java/org/wooriverygood/api/advice/exception/ReviewAccessDeniedException.java
+++ b/src/main/java/org/wooriverygood/api/review/exception/ReviewAccessDeniedException.java
@@ -1,6 +1,6 @@
-package org.wooriverygood.api.advice.exception;
+package org.wooriverygood.api.review.exception;
-import org.wooriverygood.api.advice.exception.general.BadRequestException;
+import org.wooriverygood.api.global.error.exception.BadRequestException;
public class ReviewAccessDeniedException extends BadRequestException {
diff --git a/src/main/java/org/wooriverygood/api/advice/exception/general/ReviewNotFoundException.java b/src/main/java/org/wooriverygood/api/review/exception/ReviewNotFoundException.java
similarity index 64%
rename from src/main/java/org/wooriverygood/api/advice/exception/general/ReviewNotFoundException.java
rename to src/main/java/org/wooriverygood/api/review/exception/ReviewNotFoundException.java
index 598d5af..2ae8736 100644
--- a/src/main/java/org/wooriverygood/api/advice/exception/general/ReviewNotFoundException.java
+++ b/src/main/java/org/wooriverygood/api/review/exception/ReviewNotFoundException.java
@@ -1,4 +1,6 @@
-package org.wooriverygood.api.advice.exception.general;
+package org.wooriverygood.api.review.exception;
+
+import org.wooriverygood.api.global.error.exception.NotFoundException;
public class ReviewNotFoundException extends NotFoundException {
private static final String MESSAGE = "댓글을 찾을 수 없습니다.";
diff --git a/src/main/java/org/wooriverygood/api/review/repository/ReviewLikeRepository.java b/src/main/java/org/wooriverygood/api/review/repository/ReviewLikeRepository.java
index da7077e..f9f5817 100644
--- a/src/main/java/org/wooriverygood/api/review/repository/ReviewLikeRepository.java
+++ b/src/main/java/org/wooriverygood/api/review/repository/ReviewLikeRepository.java
@@ -1,6 +1,7 @@
package org.wooriverygood.api.review.repository;
import org.springframework.data.jpa.repository.JpaRepository;
+import org.wooriverygood.api.member.domain.Member;
import org.wooriverygood.api.review.domain.Review;
import org.wooriverygood.api.review.domain.ReviewLike;
@@ -8,7 +9,7 @@
import java.util.Optional;
public interface ReviewLikeRepository extends JpaRepository {
- Optional findByReviewAndUsername(Review review, String username);
- boolean existsByReviewAndUsername(Review review, String username);
+ Optional findByReviewAndMember(Review review, Member member);
+ boolean existsByReviewAndMember(Review review, Member member);
void deleteAllByReview(Review review);
}
diff --git a/src/main/java/org/wooriverygood/api/review/repository/ReviewRepository.java b/src/main/java/org/wooriverygood/api/review/repository/ReviewRepository.java
index 99b5bd7..b2957a1 100644
--- a/src/main/java/org/wooriverygood/api/review/repository/ReviewRepository.java
+++ b/src/main/java/org/wooriverygood/api/review/repository/ReviewRepository.java
@@ -4,6 +4,7 @@
import org.springframework.data.jpa.repository.Modifying;
import org.springframework.data.jpa.repository.Query;
import org.springframework.transaction.annotation.Transactional;
+import org.wooriverygood.api.member.domain.Member;
import org.wooriverygood.api.review.domain.Review;
import java.util.List;
@@ -13,9 +14,9 @@ public interface ReviewRepository extends JpaRepository {
List findAllByCourseId(Long courseId);
- List findByAuthorEmail(String author);
+ Optional findTopByMemberOrderByCreatedAtDesc(Member member);
- Optional findTopByAuthorEmailOrderByCreatedAtDesc(String author);
+ void deleteAllInBatch();
@Transactional
@Modifying(clearAutomatically = true)
@@ -27,13 +28,5 @@ public interface ReviewRepository extends JpaRepository {
@Query(value = "UPDATE reviews SET like_count = like_count - 1 WHERE review_id = :reviewId", nativeQuery = true)
void decreaseLikeCount(Long reviewId);
- @Transactional
- @Modifying(clearAutomatically = true)
- @Query(value = "UPDATE courses SET review_count = review_count + 1 WHERE course_id = :courseId", nativeQuery = true)
- void increaseReviewCount(Long courseId);
- @Transactional
- @Modifying(clearAutomatically = true)
- @Query(value = "UPDATE courses SET review_count = review_count - 1 WHERE course_id = :courseId", nativeQuery = true)
- void decreaseReviewCount(Long courseId);
}
diff --git a/src/main/java/org/wooriverygood/api/review/service/ReviewService.java b/src/main/java/org/wooriverygood/api/review/service/ReviewService.java
deleted file mode 100644
index 7fc1eb4..0000000
--- a/src/main/java/org/wooriverygood/api/review/service/ReviewService.java
+++ /dev/null
@@ -1,160 +0,0 @@
-package org.wooriverygood.api.review.service;
-
-import lombok.RequiredArgsConstructor;
-import org.springframework.stereotype.Service;
-import org.springframework.transaction.annotation.Transactional;
-import org.wooriverygood.api.advice.exception.CourseNotFoundException;
-import org.wooriverygood.api.advice.exception.general.ReviewNotFoundException;
-import org.wooriverygood.api.course.domain.Courses;
-import org.wooriverygood.api.course.repository.CourseRepository;
-import org.wooriverygood.api.review.domain.Review;
-import org.wooriverygood.api.review.domain.ReviewLike;
-import org.wooriverygood.api.review.dto.*;
-import org.wooriverygood.api.review.repository.ReviewLikeRepository;
-import org.wooriverygood.api.review.repository.ReviewRepository;
-import org.wooriverygood.api.support.AuthInfo;
-
-import java.time.LocalDateTime;
-import java.time.temporal.ChronoUnit;
-import java.util.List;
-import java.util.Optional;
-
-@Service
-@RequiredArgsConstructor
-@Transactional(readOnly = true)
-public class ReviewService {
- private final ReviewRepository reviewRepository;
- private final CourseRepository courseRepository;
- private final ReviewLikeRepository reviewLikeRepository;
-
- public List findAllReviewsByCourseId(Long courseId, AuthInfo authInfo) {
- Courses course = courseRepository.findById(courseId)
- .orElseThrow(CourseNotFoundException::new);
- List reviews = reviewRepository.findAllByCourseId(courseId);
-
- if(authInfo.getUsername() == null) {
- return reviews.stream()
- .map(review -> ReviewResponse.from(review, false, reviewLikeRepository.existsByReviewAndUsername(review, authInfo.getUsername())))
- .toList();
- }
- return reviews.stream()
- .map(review -> ReviewResponse.from(review, review.isSameAuthor(authInfo.getUsername()), reviewLikeRepository.existsByReviewAndUsername(review, authInfo.getUsername())))
- .toList();
- }
-
- @Transactional
- public NewReviewResponse addReview(AuthInfo authInfo, Long courseId, NewReviewRequest newReviewRequest) {
- Courses course = courseRepository.findById(courseId)
- .orElseThrow(CourseNotFoundException::new);
- Review review = Review.builder()
- .reviewTitle(newReviewRequest.getReview_title())
- .course(course)
- .instructorName(newReviewRequest.getInstructor_name())
- .takenSemyr(newReviewRequest.getTaken_semyr())
- .reviewContent(newReviewRequest.getReview_content())
- .grade(newReviewRequest.getGrade())
- .authorEmail(authInfo.getUsername())
- .build();
- Review saved = reviewRepository.save(review);
- reviewRepository.increaseReviewCount(review.getCourse().getId());
- return createResponse(saved);
- }
-
- @Transactional
- public ReviewLikeResponse likeReview(Long reviewId, AuthInfo authInfo) {
- Review review = reviewRepository.findById(reviewId)
- .orElseThrow(ReviewNotFoundException::new);
-
- Optional reviewLike = reviewLikeRepository.findByReviewAndUsername(review, authInfo.getUsername());
-
- if(reviewLike.isEmpty()) {
- addReviewLike(review, authInfo.getUsername());
- return createReviewLikeResponse(review, true);
- }
-
- deleteReviewLike(review, reviewLike.get());
- return createReviewLikeResponse(review, false);
-
- }
-
- private void addReviewLike(Review review, String username) {
- ReviewLike reviewLike = ReviewLike.builder()
- .review(review)
- .username(username)
- .build();
-
- review.addReviewLike(reviewLike);
- reviewLikeRepository.save(reviewLike);
- reviewRepository.increaseLikeCount(review.getId());
- }
-
- private void deleteReviewLike(Review review, ReviewLike reviewLike) {
- review.deleteReviewLike(reviewLike);
- reviewRepository.decreaseLikeCount(review.getId());
- }
-
- public List findMyReviews(AuthInfo authInfo) {
- List reviews= reviewRepository.findByAuthorEmail(authInfo.getUsername());
- return reviews.stream().map(review -> ReviewResponse.from(review, true, reviewLikeRepository.existsByReviewAndUsername(review, authInfo.getUsername()))).toList();
- }
-
-
- private NewReviewResponse createResponse(Review review) {
- return NewReviewResponse.builder()
- .review_id(review.getId())
- .review_title(review.getReviewTitle())
- .instructor_name(review.getInstructorName())
- .taken_semyr(review.getTakenSemyr())
- .review_content(review.getReviewContent())
- .grade(review.getGrade())
- .author_email(review.getAuthorEmail())
- .build();
- }
-
- private ReviewLikeResponse createReviewLikeResponse(Review review, boolean liked) {
- int likeCount = review.getLikeCount() + (liked ? 1 : -1);
- return ReviewLikeResponse.builder()
- .like_count(likeCount)
- .liked(liked)
- .build();
- }
-
-
- @Transactional
- public ReviewUpdateResponse updateReview(Long reviewId, ReviewUpdateRequest reviewUpdateRequest, AuthInfo authInfo) {
- Review review = reviewRepository.findById(reviewId)
- .orElseThrow(ReviewNotFoundException::new);
-// review.validateAuthor(authInfo.getUsername());
-
- review.updateReview(reviewUpdateRequest.getReview_title(), reviewUpdateRequest.getInstructor_name(), reviewUpdateRequest.getTaken_semyr(), reviewUpdateRequest.getReview_content(), reviewUpdateRequest.getGrade(), authInfo.getUsername());
-
- return ReviewUpdateResponse.builder()
- .review_id(review.getId())
- .build();
- }
-
- @Transactional
- public ReviewDeleteResponse deleteReview(Long reviewId, AuthInfo authInfo) {
- Review review = reviewRepository.findById(reviewId)
- .orElseThrow(ReviewNotFoundException::new);
- review.validateAuthor(authInfo.getUsername());
-
- reviewLikeRepository.deleteAllByReview(review);
- reviewRepository.delete(review);
- reviewRepository.decreaseReviewCount(review.getCourse().getId());
-
- return ReviewDeleteResponse.builder()
- .review_id(reviewId)
- .build();
- }
-
- public boolean canAccessReviews(AuthInfo authInfo) {
- Optional review = reviewRepository.findTopByAuthorEmailOrderByCreatedAtDesc(authInfo.getUsername());
- if (review.isEmpty()) {
- return false;
- }
- LocalDateTime now = LocalDateTime.now();
- long distance = ChronoUnit.MONTHS.between(review.get().getCreatedAt(), now);
- return distance <= 6;
- }
-}
diff --git a/src/test/java/org/wooriverygood/api/comment/controller/CommentControllerTest.java b/src/test/java/org/wooriverygood/api/comment/api/CommentApiTest.java
similarity index 70%
rename from src/test/java/org/wooriverygood/api/comment/controller/CommentControllerTest.java
rename to src/test/java/org/wooriverygood/api/comment/api/CommentApiTest.java
index e34c000..b364b5b 100644
--- a/src/test/java/org/wooriverygood/api/comment/controller/CommentControllerTest.java
+++ b/src/test/java/org/wooriverygood/api/comment/api/CommentApiTest.java
@@ -1,67 +1,67 @@
-package org.wooriverygood.api.comment.controller;
+package org.wooriverygood.api.comment.api;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
-import org.mockito.Mockito;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
-import org.wooriverygood.api.advice.exception.AuthorizationException;
-import org.wooriverygood.api.advice.exception.ReplyDepthException;
+import org.wooriverygood.api.global.error.exception.AuthorizationException;
+import org.wooriverygood.api.comment.exception.ReplyDepthException;
import org.wooriverygood.api.comment.dto.*;
+import org.wooriverygood.api.member.domain.Member;
import org.wooriverygood.api.post.domain.Post;
import org.wooriverygood.api.post.domain.PostCategory;
-import org.wooriverygood.api.support.AuthInfo;
-import org.wooriverygood.api.util.ControllerTest;
+import org.wooriverygood.api.global.auth.AuthInfo;
+import org.wooriverygood.api.util.ApiTest;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.List;
import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.Mockito.doNothing;
-import static org.mockito.Mockito.doThrow;
+import static org.mockito.Mockito.*;
import static org.springframework.restdocs.mockmvc.MockMvcRestDocumentation.document;
+class CommentApiTest extends ApiTest {
-class CommentControllerTest extends ControllerTest {
+ private List responses = new ArrayList<>();
- List responses = new ArrayList<>();
-
- Post post = Post.builder()
+ private Post post = Post.builder()
.id(1L)
.category(PostCategory.OFFER)
.title("title6")
.content("content6")
- .author("user-3333")
+ .member(new Member(1L, "username"))
.comments(new ArrayList<>())
.postLikes(new ArrayList<>())
.build();
@BeforeEach
void setUp() {
- for (int i = 1; i <= 4; i++) {
+ for (long i = 1; i <= 4; i++) {
responses.add(CommentResponse.builder()
- .comment_id((long) i)
- .comment_content("content" + i)
- .comment_author("user-"+(i % 5))
- .post_id(post.getId())
- .comment_likes(i + 8)
- .comment_time(LocalDateTime.now())
+ .commentId(i)
+ .commentContent("content" + i)
+ .postId(post.getId())
+ .commentLikeCount((int) i + 8)
+ .commentTime(LocalDateTime.now())
.liked(i % 3 == 0)
.replies(new ArrayList<>())
.updated(i % 2 == 0)
.reported(false)
+ .memberId(1L)
+ .isMine(true)
.build());
}
for (int i = 12; i <= 15; i++) {
responses.get(2).getReplies()
.add(ReplyResponse.builder()
- .reply_id((long) i)
- .reply_content("reply content " + i)
- .reply_author("user-" + (i % 2))
- .reply_likes(i - 6)
- .reply_time(LocalDateTime.now())
+ .replyId((long) i)
+ .replyContent("reply content " + i)
+ .memberId(1L)
+ .isMine(true)
+ .replyLikeCount(i - 6)
+ .replyTime(LocalDateTime.now())
.liked(false)
.updated(i % 2 == 0)
.reported(false)
@@ -72,13 +72,13 @@ void setUp() {
@Test
@DisplayName("특정 게시글의 댓글 조회 요청을 받으면 댓글들을 반환한다.")
void findAllCommentsByPostId() {
- Mockito.when(commentService.findAllComments(any(Long.class), any(AuthInfo.class)))
- .thenReturn(responses);
+ when(commentFindService.findAllCommentsByPostId(anyLong(), any(AuthInfo.class)))
+ .thenReturn(new CommentsResponse(responses));
restDocs
.contentType(MediaType.APPLICATION_JSON_VALUE)
.header("Authorization", "Bearer aws-cognito-access-token")
- .when().get("/community/1/comments")
+ .when().get("/posts/1/comments")
.then().log().all()
.assertThat()
.apply(document("comments/find/success"))
@@ -92,20 +92,11 @@ void addComment() {
.content("content51")
.build();
- NewCommentResponse response = NewCommentResponse.builder()
- .comment_id(51L)
- .content("content51")
- .author(testAuthInfo.getUsername())
- .build();
-
- Mockito.when(commentService.addComment(any(AuthInfo.class), any(Long.class), any(NewCommentRequest.class)))
- .thenReturn(response);
-
restDocs
.contentType(MediaType.APPLICATION_JSON_VALUE)
.header("Authorization", "Bearer aws-cognito-access-token")
.body(request)
- .when().post("/community/51/comments")
+ .when().post("/posts/51/comments")
.then().log().all()
.assertThat()
.apply(document("comments/create/success"))
@@ -115,9 +106,9 @@ void addComment() {
@Test
@DisplayName("특정 댓글의 좋아요를 1 올리거나 내린다.")
void likeComment() {
- Mockito.when(commentService.likeComment(any(Long.class), any(AuthInfo.class)))
+ when(commentLikeToggleService.likeComment(any(Long.class), any(AuthInfo.class)))
.thenReturn(CommentLikeResponse.builder()
- .like_count(5)
+ .likeCount(5)
.liked(false)
.build());
@@ -138,11 +129,6 @@ void updateComment() {
.content("new comment content")
.build();
- Mockito.when(commentService.updateComment(any(Long.class), any(CommentUpdateRequest.class), any(AuthInfo.class)))
- .thenReturn(CommentUpdateResponse.builder()
- .comment_id(2L)
- .build());
-
restDocs
.contentType(MediaType.APPLICATION_JSON_VALUE)
.header("Authorization", "Bearer aws-cognito-access-token")
@@ -151,7 +137,7 @@ void updateComment() {
.then().log().all()
.assertThat()
.apply(document("comments/update/success"))
- .statusCode(HttpStatus.OK.value());
+ .statusCode(HttpStatus.NO_CONTENT.value());
}
@Test
@@ -178,8 +164,9 @@ void updateComment_exception_noAuth() {
.content("new comment content")
.build();
- Mockito.when(commentService.updateComment(any(Long.class), any(CommentUpdateRequest.class), any(AuthInfo.class)))
- .thenThrow(new AuthorizationException());
+ doThrow(new AuthorizationException())
+ .when(commentUpdateService)
+ .updateComment(anyLong(), any(CommentUpdateRequest.class), any(AuthInfo.class));
restDocs
.contentType(MediaType.APPLICATION_JSON_VALUE)
@@ -195,11 +182,6 @@ void updateComment_exception_noAuth() {
@Test
@DisplayName("권한이 있는 댓글을 삭제한다.")
void deleteComment() {
- Mockito.when(commentService.deleteComment(any(Long.class), any(AuthInfo.class)))
- .thenReturn(CommentDeleteResponse.builder()
- .comment_id(3L)
- .build());
-
restDocs
.contentType(MediaType.APPLICATION_JSON_VALUE)
.header("Authorization", "Bearer aws-cognito-access-token")
@@ -207,14 +189,15 @@ void deleteComment() {
.then().log().all()
.assertThat()
.apply(document("comments/delete/success"))
- .statusCode(HttpStatus.OK.value());
+ .statusCode(HttpStatus.NO_CONTENT.value());
}
@Test
@DisplayName("권한이 없는 댓글을 삭제하면 404를 반환한다.")
void deleteComment_exception_noAuth() {
- Mockito.when(commentService.deleteComment(any(Long.class), any(AuthInfo.class)))
- .thenThrow(new AuthorizationException());
+ doThrow(new AuthorizationException())
+ .when(commentDeleteService)
+ .deleteComment(anyLong(), any(AuthInfo.class));
restDocs
.contentType(MediaType.APPLICATION_JSON_VALUE)
@@ -233,9 +216,6 @@ void addReply() {
.content("reply content")
.build();
- doNothing().when(commentService)
- .addReply(any(Long.class), any(NewReplyRequest.class), any(AuthInfo.class));
-
restDocs
.contentType(MediaType.APPLICATION_JSON_VALUE)
.header("Authorization", "Bearer aws-cognito-access-token")
@@ -248,14 +228,15 @@ void addReply() {
}
@Test
- @DisplayName("특정 댓글의 대댓글을 작성한다.")
+ @DisplayName("특정 대댓글의 대댓글을 작성하려고 하면, 400 에러를 반환한다.")
void addReply_exception_depth() {
NewReplyRequest request = NewReplyRequest.builder()
.content("reply content")
.build();
- doThrow(new ReplyDepthException()).when(commentService)
- .addReply(any(Long.class), any(NewReplyRequest.class), any(AuthInfo.class));
+ doThrow(new ReplyDepthException())
+ .when(commentCreateService)
+ .addReply(anyLong(), any(NewReplyRequest.class), any(AuthInfo.class));
restDocs
.contentType(MediaType.APPLICATION_JSON_VALUE)
diff --git a/src/test/java/org/wooriverygood/api/comment/application/CommentCreateServiceTest.java b/src/test/java/org/wooriverygood/api/comment/application/CommentCreateServiceTest.java
new file mode 100644
index 0000000..8826a3b
--- /dev/null
+++ b/src/test/java/org/wooriverygood/api/comment/application/CommentCreateServiceTest.java
@@ -0,0 +1,89 @@
+package org.wooriverygood.api.comment.application;
+
+import org.junit.jupiter.api.DisplayName;
+import org.junit.jupiter.api.Test;
+import org.mockito.InjectMocks;
+import org.mockito.Mock;
+import org.wooriverygood.api.comment.exception.ReplyDepthException;
+import org.wooriverygood.api.comment.domain.Comment;
+import org.wooriverygood.api.comment.dto.NewCommentRequest;
+import org.wooriverygood.api.comment.dto.NewReplyRequest;
+import org.wooriverygood.api.comment.repository.CommentRepository;
+import org.wooriverygood.api.member.repository.MemberRepository;
+import org.wooriverygood.api.post.repository.PostRepository;
+
+import java.util.Optional;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.assertj.core.api.Assertions.assertThatThrownBy;
+import static org.junit.jupiter.api.Assertions.assertAll;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyLong;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+class CommentCreateServiceTest extends CommentServiceTest {
+
+ @InjectMocks
+ private CommentCreateService commentCreateService;
+
+ @Mock
+ private PostRepository postRepository;
+
+ @Mock
+ private CommentRepository commentRepository;
+
+ @Mock
+ private MemberRepository memberRepository;
+
+
+ @Test
+ @DisplayName("특정 게시글의 댓글을 작성한다.")
+ void addComment() {
+ NewCommentRequest request = NewCommentRequest.builder()
+ .content("comment content")
+ .build();
+ when(memberRepository.findById(anyLong()))
+ .thenReturn(Optional.ofNullable(member));
+ when(postRepository.findById(anyLong()))
+ .thenReturn(Optional.ofNullable(post));
+
+ commentCreateService.addComment(authInfo, post.getId(), request);
+
+ verify(commentRepository).save(any(Comment.class));
+ }
+
+ @Test
+ @DisplayName("특정 댓글의 대댓글을 작성한다.")
+ void addReply() {
+ NewReplyRequest request = NewReplyRequest.builder()
+ .content("reply content")
+ .build();
+ when(commentRepository.findById(anyLong()))
+ .thenReturn(Optional.ofNullable(commentWithoutReply));
+ when(memberRepository.findById(anyLong()))
+ .thenReturn(Optional.ofNullable(member));
+
+ commentCreateService.addReply(commentWithoutReply.getId(), request, authInfo);
+
+ assertAll(
+ () -> assertThat(commentWithoutReply.getReplies().size()).isEqualTo(1),
+ () -> assertThat(commentWithoutReply.getReplies().get(0).getContent()).isEqualTo(request.getContent()),
+ () -> assertThat(commentWithoutReply.getReplies().get(0).getParent()).isEqualTo(commentWithoutReply)
+ );
+ }
+
+ @Test
+ @DisplayName("대댓글의 대댓글을 작성할 수 없다.")
+ void addReply_exception_depth() {
+ NewReplyRequest request = NewReplyRequest.builder()
+ .content("reply content")
+ .build();
+ when(commentRepository.findById(anyLong()))
+ .thenReturn(Optional.ofNullable(reply));
+
+ assertThatThrownBy(() -> commentCreateService.addReply(reply.getId(), request, authInfo))
+ .isInstanceOf(ReplyDepthException.class);
+ }
+
+}
\ No newline at end of file
diff --git a/src/test/java/org/wooriverygood/api/comment/application/CommentDeleteServiceTest.java b/src/test/java/org/wooriverygood/api/comment/application/CommentDeleteServiceTest.java
new file mode 100644
index 0000000..e7075a1
--- /dev/null
+++ b/src/test/java/org/wooriverygood/api/comment/application/CommentDeleteServiceTest.java
@@ -0,0 +1,126 @@
+package org.wooriverygood.api.comment.application;
+
+import org.junit.jupiter.api.DisplayName;
+import org.junit.jupiter.api.Test;
+import org.mockito.InjectMocks;
+import org.mockito.Mock;
+import org.wooriverygood.api.comment.repository.CommentLikeRepository;
+import org.wooriverygood.api.comment.repository.CommentRepository;
+import org.wooriverygood.api.global.error.exception.AuthorizationException;
+import org.wooriverygood.api.member.domain.Member;
+import org.wooriverygood.api.member.repository.MemberRepository;
+
+import java.util.Optional;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.assertj.core.api.Assertions.assertThatThrownBy;
+import static org.junit.jupiter.api.Assertions.assertAll;
+import static org.mockito.ArgumentMatchers.anyLong;
+import static org.mockito.Mockito.*;
+
+class CommentDeleteServiceTest extends CommentServiceTest {
+
+ @InjectMocks
+ private CommentDeleteService commentDeleteService;
+
+ @Mock
+ private CommentRepository commentRepository;
+
+ @Mock
+ private CommentLikeRepository commentLikeRepository;
+
+ @Mock
+ private MemberRepository memberRepository;
+
+
+ @Test
+ @DisplayName("권한이 있는 댓글을 삭제한다.")
+ void deleteComment() {
+ when(memberRepository.findById(anyLong()))
+ .thenReturn(Optional.ofNullable(member));
+ when(commentRepository.findById(anyLong()))
+ .thenReturn(Optional.ofNullable(comment));
+ comment.deleteReply(reply);
+
+ commentDeleteService.deleteComment(comment.getId(), authInfo);
+
+ assertAll(
+ () -> verify(commentLikeRepository).deleteAllByComment(comment),
+ () -> verify(commentRepository).delete(comment)
+ );
+ }
+
+ @Test
+ @DisplayName("권한이 있는 대댓글을 삭제한다.")
+ void deleteReply() {
+ when(memberRepository.findById(anyLong()))
+ .thenReturn(Optional.ofNullable(member));
+ when(commentRepository.findById(anyLong()))
+ .thenReturn(Optional.ofNullable(reply));
+
+ commentDeleteService.deleteComment(reply.getId(), authInfo);
+
+ assertAll(
+ () -> verify(commentLikeRepository).deleteAllByComment(reply),
+ () -> verify(commentRepository).delete(reply)
+ );
+ }
+
+ @Test
+ @DisplayName("권한이 없는 대댓글을 삭제할 수 없다.")
+ void deleteReply_exception_noAuth() {
+ when(memberRepository.findById(anyLong()))
+ .thenReturn(Optional.of(new Member(5L, "username")));
+ when(commentRepository.findById(anyLong()))
+ .thenReturn(Optional.ofNullable(reply));
+
+ assertThatThrownBy(() -> commentDeleteService.deleteComment(reply.getId(), authInfo))
+ .isInstanceOf(AuthorizationException.class);
+ }
+
+ @Test
+ @DisplayName("부모 댓글을 삭제해도 대댓글은 남아있다.")
+ void deleteComment_keepChildren() {
+ when(memberRepository.findById(anyLong()))
+ .thenReturn(Optional.ofNullable(member));
+ when(commentRepository.findById(anyLong()))
+ .thenReturn(Optional.ofNullable(comment));
+
+ commentDeleteService.deleteComment(comment.getId(), authInfo);
+
+ assertAll(
+ () -> assertThat(comment.isSoftRemoved()).isEqualTo(true),
+ () -> assertThat(comment.canDelete()).isEqualTo(false)
+ );
+ }
+
+ @Test
+ @DisplayName("특정 대댓글 삭제 후, 삭제 예정으로 처리되고 대댓글이 없는 부모 댓글을 삭제한다.")
+ void deletePrentAndReply() {
+ when(memberRepository.findById(anyLong()))
+ .thenReturn(Optional.ofNullable(member));
+ when(commentRepository.findById(anyLong()))
+ .thenReturn(Optional.ofNullable(reply));
+ comment.willBeDeleted();
+
+ commentDeleteService.deleteComment(reply.getId(), authInfo);
+
+ assertAll(
+ () -> verify(commentRepository).delete(reply),
+ () -> verify(commentRepository).delete(comment)
+ );
+ }
+
+ @Test
+ @DisplayName("권한이 없는 댓글은 삭제할 수 없다")
+ void deleteComment_exception_noAuth() {
+ when(memberRepository.findById(anyLong()))
+ .thenReturn(Optional.of(new Member(5L, "username")));
+ when(commentRepository.findById(anyLong()))
+ .thenReturn(Optional.ofNullable(comment));
+
+ assertThatThrownBy(() -> commentDeleteService.deleteComment(comment.getId(), authInfo))
+ .isInstanceOf(AuthorizationException.class);
+ }
+
+}
\ No newline at end of file
diff --git a/src/test/java/org/wooriverygood/api/comment/application/CommentFindServiceTest.java b/src/test/java/org/wooriverygood/api/comment/application/CommentFindServiceTest.java
new file mode 100644
index 0000000..2fe1f11
--- /dev/null
+++ b/src/test/java/org/wooriverygood/api/comment/application/CommentFindServiceTest.java
@@ -0,0 +1,122 @@
+package org.wooriverygood.api.comment.application;
+
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.DisplayName;
+import org.junit.jupiter.api.Test;
+import org.mockito.InjectMocks;
+import org.mockito.Mock;
+import org.wooriverygood.api.comment.domain.Comment;
+import org.wooriverygood.api.comment.dto.CommentsResponse;
+import org.wooriverygood.api.comment.repository.CommentLikeRepository;
+import org.wooriverygood.api.comment.repository.CommentRepository;
+import org.wooriverygood.api.member.domain.Member;
+import org.wooriverygood.api.member.repository.MemberRepository;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Optional;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.mockito.ArgumentMatchers.*;
+import static org.mockito.Mockito.when;
+
+class CommentFindServiceTest extends CommentServiceTest {
+
+ @InjectMocks
+ private CommentFindService commentFindService;
+
+ @Mock
+ private CommentRepository commentRepository;
+
+ @Mock
+ private CommentLikeRepository commentLikeRepository;
+
+ @Mock
+ private MemberRepository memberRepository;
+
+ private final int COMMENT_COUNT = 10;
+
+ private List comments = new ArrayList<>();
+
+
+ @BeforeEach
+ void setUp() {
+ for (long i = 0; i < COMMENT_COUNT; i++) {
+ Comment comment = Comment.builder()
+ .id(i)
+ .content("comment" + i)
+ .post(post)
+ .member(member)
+ .build();
+ comments.add(comment);
+ }
+ }
+
+ @Test
+ @DisplayName("유효한 id를 통해 특정 게시글의 댓글들을 불러온다.")
+ void findAllCommentsByPostId() {
+ when(memberRepository.findById(anyLong()))
+ .thenReturn(Optional.ofNullable(member));
+ when(commentRepository.findAllByPostId(anyLong()))
+ .thenReturn(comments);
+ when(commentLikeRepository.existsByCommentAndMember(any(Comment.class), any(Member.class)))
+ .thenReturn(true);
+
+ CommentsResponse response = commentFindService.findAllCommentsByPostId(2L, authInfo);
+
+ assertThat(response.getComments().size()).isEqualTo(COMMENT_COUNT);
+ }
+
+ @Test
+ @DisplayName("삭제 처리될 댓글의 내용은 비어있다.")
+ void findAllCommentsByPostId_softRemoved() {
+ comments = new ArrayList<>();
+ comments.add(Comment.builder()
+ .id(1L)
+ .content("Content")
+ .post(post)
+ .member(member)
+ .softRemoved(true)
+ .build());
+ when(memberRepository.findById(anyLong()))
+ .thenReturn(Optional.ofNullable(member));
+ when(commentRepository.findAllByPostId(anyLong()))
+ .thenReturn(comments);
+
+ CommentsResponse response = commentFindService.findAllCommentsByPostId(2L, authInfo);
+
+ assertThat(response.getComments().get(0).getCommentContent()).isEqualTo(null);
+ }
+
+ @Test
+ @DisplayName("대댓글만 불러올 수 없다.")
+ void findAllCommentsByPostId_cannot_only_reply() {
+ comments = new ArrayList<>();
+ comments.add(reply);
+ when(memberRepository.findById(anyLong()))
+ .thenReturn(Optional.ofNullable(member));
+ when(commentRepository.findAllByPostId(anyLong()))
+ .thenReturn(comments);
+
+ CommentsResponse response = commentFindService.findAllCommentsByPostId(2L, authInfo);
+
+ assertThat(response.getComments().size()).isEqualTo(0);
+ }
+
+ @Test
+ @DisplayName("댓글과 대댓글들을 모두 불러온다.")
+ void findAllCommentsByPostId_withReplies() {
+ comments = new ArrayList<>();
+ comments.add(comment);
+ comments.add(reply);
+ when(memberRepository.findById(anyLong()))
+ .thenReturn(Optional.ofNullable(member));
+ when(commentRepository.findAllByPostId(anyLong()))
+ .thenReturn(comments);
+
+ CommentsResponse response = commentFindService.findAllCommentsByPostId(2L, authInfo);
+
+ assertThat(response.getComments().get(0).getReplies().size()).isEqualTo(1);
+ }
+
+}
\ No newline at end of file
diff --git a/src/test/java/org/wooriverygood/api/comment/application/CommentLikeToggleServiceTest.java b/src/test/java/org/wooriverygood/api/comment/application/CommentLikeToggleServiceTest.java
new file mode 100644
index 0000000..da1da3f
--- /dev/null
+++ b/src/test/java/org/wooriverygood/api/comment/application/CommentLikeToggleServiceTest.java
@@ -0,0 +1,78 @@
+package org.wooriverygood.api.comment.application;
+
+import org.junit.jupiter.api.DisplayName;
+import org.junit.jupiter.api.Test;
+import org.mockito.InjectMocks;
+import org.mockito.Mock;
+import org.wooriverygood.api.comment.domain.Comment;
+import org.wooriverygood.api.comment.domain.CommentLike;
+import org.wooriverygood.api.comment.dto.*;
+import org.wooriverygood.api.comment.repository.CommentLikeRepository;
+import org.wooriverygood.api.comment.repository.CommentRepository;
+import org.wooriverygood.api.member.domain.Member;
+import org.wooriverygood.api.member.repository.MemberRepository;
+
+import java.util.Optional;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.junit.jupiter.api.Assertions.assertAll;
+import static org.mockito.ArgumentMatchers.*;
+import static org.mockito.Mockito.when;
+
+class CommentLikeToggleServiceTest extends CommentServiceTest {
+
+ @InjectMocks
+ private CommentLikeToggleService commentLikeToggleService;
+
+ @Mock
+ private CommentRepository commentRepository;
+
+ @Mock
+ private CommentLikeRepository commentLikeRepository;
+
+ @Mock
+ private MemberRepository memberRepository;
+
+
+ @Test
+ @DisplayName("특정 댓글의 좋아요를 1 올린다.")
+ void likeComment_up() {
+ when(memberRepository.findById(anyLong()))
+ .thenReturn(Optional.ofNullable(member));
+ when(commentRepository.findById(anyLong()))
+ .thenReturn(Optional.ofNullable(comment));
+ when(commentLikeRepository.findByCommentAndMember(any(Comment.class), any(Member.class)))
+ .thenReturn(Optional.empty());
+
+ CommentLikeResponse response = commentLikeToggleService.likeComment(comment.getId(), authInfo);
+
+ assertAll(
+ () -> assertThat(response.getLikeCount()).isEqualTo(comment.getLikeCount() + 1),
+ () -> assertThat(response.isLiked()).isEqualTo(true)
+ );
+ }
+
+ @Test
+ @DisplayName("특정 댓글의 좋아요를 1 내린다.")
+ void likeComment_down() {
+ CommentLike commentLike = CommentLike.builder()
+ .id(1L)
+ .comment(comment)
+ .member(member)
+ .build();
+ when(memberRepository.findById(anyLong()))
+ .thenReturn(Optional.ofNullable(member));
+ when(commentRepository.findById(anyLong()))
+ .thenReturn(Optional.ofNullable(comment));
+ when(commentLikeRepository.findByCommentAndMember(any(Comment.class), any(Member.class)))
+ .thenReturn(Optional.ofNullable(commentLike));
+
+ CommentLikeResponse response = commentLikeToggleService.likeComment(comment.getId(), authInfo);
+
+ assertAll(
+ () -> assertThat(response.getLikeCount()).isEqualTo(comment.getLikeCount() - 1),
+ () -> assertThat(response.isLiked()).isEqualTo(false)
+ );
+ }
+
+}
\ No newline at end of file
diff --git a/src/test/java/org/wooriverygood/api/comment/application/CommentServiceTest.java b/src/test/java/org/wooriverygood/api/comment/application/CommentServiceTest.java
new file mode 100644
index 0000000..b436d21
--- /dev/null
+++ b/src/test/java/org/wooriverygood/api/comment/application/CommentServiceTest.java
@@ -0,0 +1,57 @@
+package org.wooriverygood.api.comment.application;
+
+import org.junit.jupiter.api.BeforeEach;
+import org.wooriverygood.api.comment.domain.Comment;
+import org.wooriverygood.api.post.domain.Post;
+import org.wooriverygood.api.post.domain.PostCategory;
+import org.wooriverygood.api.util.MockTest;
+
+import java.util.ArrayList;
+
+public class CommentServiceTest extends MockTest {
+
+ protected Post post;
+
+ protected Comment comment;
+
+ protected Comment commentWithoutReply;
+
+ protected Comment reply;
+
+
+ @BeforeEach
+ void dateSetUp() {
+ post = Post.builder()
+ .id(1L)
+ .category(PostCategory.OFFER)
+ .title("simple title")
+ .content("simple content")
+ .member(member)
+ .comments(new ArrayList<>())
+ .postLikes(new ArrayList<>())
+ .build();
+ comment = Comment.builder()
+ .id(1L)
+ .content("content")
+ .post(post)
+ .member(member)
+ .commentLikes(new ArrayList<>())
+ .reports(new ArrayList<>())
+ .build();
+ reply = Comment.builder()
+ .id(2L)
+ .content("content")
+ .post(post)
+ .parent(comment)
+ .member(member)
+ .build();
+ comment.addReply(reply);
+ commentWithoutReply = Comment.builder()
+ .id(3L)
+ .content("content")
+ .post(post)
+ .member(member)
+ .build();
+ }
+
+}
diff --git a/src/test/java/org/wooriverygood/api/comment/application/CommentUpdateServiceTest.java b/src/test/java/org/wooriverygood/api/comment/application/CommentUpdateServiceTest.java
new file mode 100644
index 0000000..3baea56
--- /dev/null
+++ b/src/test/java/org/wooriverygood/api/comment/application/CommentUpdateServiceTest.java
@@ -0,0 +1,67 @@
+package org.wooriverygood.api.comment.application;
+
+import org.junit.jupiter.api.DisplayName;
+import org.junit.jupiter.api.Test;
+import org.mockito.InjectMocks;
+import org.mockito.Mock;
+import org.wooriverygood.api.comment.dto.CommentUpdateRequest;
+import org.wooriverygood.api.comment.repository.CommentRepository;
+import org.wooriverygood.api.global.error.exception.AuthorizationException;
+import org.wooriverygood.api.member.domain.Member;
+import org.wooriverygood.api.member.repository.MemberRepository;
+
+import java.util.Optional;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.assertj.core.api.Assertions.assertThatThrownBy;
+import static org.junit.jupiter.api.Assertions.assertAll;
+import static org.mockito.ArgumentMatchers.anyLong;
+import static org.mockito.Mockito.when;
+
+class CommentUpdateServiceTest extends CommentServiceTest {
+
+ @InjectMocks
+ private CommentUpdateService commentUpdateService;
+
+ @Mock
+ private CommentRepository commentRepository;
+
+ @Mock
+ private MemberRepository memberRepository;
+
+
+ @Test
+ @DisplayName("권한이 있는 댓글을 수정한다.")
+ void updateComment() {
+ CommentUpdateRequest request = CommentUpdateRequest.builder()
+ .content("new comment content")
+ .build();
+ when(memberRepository.findById(anyLong()))
+ .thenReturn(Optional.ofNullable(member));
+ when(commentRepository.findById(anyLong()))
+ .thenReturn(Optional.ofNullable(comment));
+
+ commentUpdateService.updateComment(comment.getId(), request, authInfo);
+
+ assertAll(
+ () -> assertThat(comment.isUpdated()).isEqualTo(true),
+ () -> assertThat(comment.getContent()).isEqualTo(request.getContent())
+ );
+ }
+
+ @Test
+ @DisplayName("권한이 없는 댓글을 수정할 수 없다.")
+ void updateComment_exception_noAuth() {
+ CommentUpdateRequest request = CommentUpdateRequest.builder()
+ .content("new comment content")
+ .build();
+ when(memberRepository.findById(anyLong()))
+ .thenReturn(Optional.ofNullable(new Member(5L, "username")));
+ when(commentRepository.findById(anyLong()))
+ .thenReturn(Optional.ofNullable(comment));
+
+ assertThatThrownBy(() -> commentUpdateService.updateComment(comment.getId(), request, authInfo))
+ .isInstanceOf(AuthorizationException.class);
+ }
+
+}
\ No newline at end of file
diff --git a/src/test/java/org/wooriverygood/api/comment/service/CommentServiceTest.java b/src/test/java/org/wooriverygood/api/comment/service/CommentServiceTest.java
deleted file mode 100644
index f372ae6..0000000
--- a/src/test/java/org/wooriverygood/api/comment/service/CommentServiceTest.java
+++ /dev/null
@@ -1,303 +0,0 @@
-package org.wooriverygood.api.comment.service;
-
-import org.assertj.core.api.Assertions;
-import org.junit.jupiter.api.BeforeEach;
-import org.junit.jupiter.api.DisplayName;
-import org.junit.jupiter.api.Test;
-import org.junit.jupiter.api.extension.ExtendWith;
-import org.mockito.InjectMocks;
-import org.mockito.Mock;
-import org.mockito.Mockito;
-import org.mockito.junit.jupiter.MockitoExtension;
-import org.wooriverygood.api.advice.exception.AuthorizationException;
-import org.wooriverygood.api.advice.exception.ReplyDepthException;
-import org.wooriverygood.api.comment.domain.Comment;
-import org.wooriverygood.api.comment.domain.CommentLike;
-import org.wooriverygood.api.comment.dto.*;
-import org.wooriverygood.api.comment.repository.CommentLikeRepository;
-import org.wooriverygood.api.comment.repository.CommentRepository;
-import org.wooriverygood.api.post.domain.Post;
-import org.wooriverygood.api.post.domain.PostCategory;
-import org.wooriverygood.api.post.repository.PostRepository;
-import org.wooriverygood.api.support.AuthInfo;
-
-import java.util.ArrayList;
-import java.util.List;
-import java.util.Optional;
-
-import static org.mockito.ArgumentMatchers.any;
-
-@ExtendWith(MockitoExtension.class)
-class CommentServiceTest {
-
- @InjectMocks
- private CommentService commentService;
-
- @Mock
- private CommentRepository commentRepository;
-
- @Mock
- private PostRepository postRepository;
-
- @Mock
- private CommentLikeRepository commentLikeRepository;
-
- private final int COMMENT_COUNT = 10;
-
- List comments = new ArrayList<>();
-
- Post singlePost = Post.builder()
- .id(6L)
- .category(PostCategory.OFFER)
- .title("title6")
- .content("content6")
- .author("author6")
- .comments(new ArrayList<>())
- .postLikes(new ArrayList<>())
- .build();
-
- AuthInfo authInfo = AuthInfo.builder()
- .sub("22222-34534-123")
- .username("22222-34534-123")
- .build();
-
- Comment singleComment = Comment.builder()
- .id(2L)
- .post(singlePost)
- .content("comment content")
- .author(authInfo.getUsername())
- .commentLikes(new ArrayList<>())
- .build();
-
- Comment reply = Comment.builder()
- .id(3L)
- .post(singlePost)
- .content("reply content")
- .author(authInfo.getUsername())
- .commentLikes(new ArrayList<>())
- .parent(singleComment)
- .build();
-
-
- @BeforeEach
- void setUpPosts() {
- singleComment.getChildren().add(reply);
-
- for (int i = 0; i < COMMENT_COUNT; i++) {
- Comment comment = Comment.builder()
- .id((long) i)
- .content("comment" + i)
- .author("author" + i)
- .post(singlePost)
- .build();
- comments.add(comment);
- }
- }
-
- @Test
- @DisplayName("유효한 id를 통해 특정 게시글의 댓글들을 불러온다.")
- void findAllCommentsByPostId() {
- Mockito.when(commentRepository.findAllByPostId(any()))
- .thenReturn(comments);
-
- List responses = commentService.findAllComments(2L, authInfo);
-
- Assertions.assertThat(responses.size()).isEqualTo(COMMENT_COUNT);
- }
-
- @Test
- @DisplayName("특정 게시글의 댓글을 작성한다.")
- void addComment() {
- NewCommentRequest newCommentRequest = NewCommentRequest.builder()
- .content("comment content")
- .build();
-
- Mockito.when(commentRepository.save(any(Comment.class)))
- .thenReturn(Comment.builder()
- .author(authInfo.getUsername())
- .content(newCommentRequest.getContent())
- .build());
-
- Mockito.when(postRepository.findById(any()))
- .thenReturn(Optional.ofNullable(singlePost));
-
- NewCommentResponse response = commentService.addComment(authInfo, 1L, newCommentRequest);
-
- Assertions.assertThat(response.getAuthor()).isEqualTo(authInfo.getUsername());
- Assertions.assertThat(response.getContent()).isEqualTo(newCommentRequest.getContent());
- }
-
- @Test
- @DisplayName("특정 댓글의 좋아요를 1 올린다.")
- void likeComment_up() {
- Mockito.when(commentRepository.findById(any(Long.class)))
- .thenReturn(Optional.ofNullable(singleComment));
-
- CommentLikeResponse response = commentService.likeComment(singleComment.getId(), authInfo);
-
- Assertions.assertThat(response.getLike_count()).isEqualTo(singleComment.getLikeCount() + 1);
- Assertions.assertThat(response.isLiked()).isEqualTo(true);
- }
-
- @Test
- @DisplayName("특정 댓글의 좋아요를 1 내린다.")
- void likeComment_down() {
- CommentLike commentLike = CommentLike.builder()
- .id(2L)
- .comment(singleComment)
- .username(authInfo.getUsername())
- .build();
- Mockito.when(commentRepository.findById(any(Long.class)))
- .thenReturn(Optional.ofNullable(singleComment));
- Mockito.when(commentLikeRepository.findByCommentAndUsername(any(Comment.class), any(String.class)))
- .thenReturn(Optional.ofNullable(commentLike));
-
- CommentLikeResponse response = commentService.likeComment(singleComment.getId(), authInfo);
-
- Assertions.assertThat(response.getLike_count()).isEqualTo(singleComment.getLikeCount() - 1);
- Assertions.assertThat(response.isLiked()).isEqualTo(false);
- }
-
- @Test
- @DisplayName("권한이 있는 댓글을 수정한다.")
- void updateComment() {
- CommentUpdateRequest request = CommentUpdateRequest.builder()
- .content("new comment content")
- .build();
-
- Mockito.when(commentRepository.findById(any(Long.class)))
- .thenReturn(Optional.ofNullable(singleComment));
-
- CommentUpdateResponse response = commentService.updateComment(singleComment.getId(), request, authInfo);
-
- Assertions.assertThat(response.getComment_id()).isEqualTo(singleComment.getId());
- Assertions.assertThat(singleComment.isUpdated()).isEqualTo(true);
- }
-
- @Test
- @DisplayName("권한이 없는 댓글을 수정할 수 없다.")
- void updateComment_exception_noAuth() {
- CommentUpdateRequest request = CommentUpdateRequest.builder()
- .content("new comment content")
- .build();
- AuthInfo noAuthInfo = AuthInfo.builder()
- .sub("no")
- .username("no")
- .build();
-
- Mockito.when(commentRepository.findById(any(Long.class)))
- .thenReturn(Optional.ofNullable(singleComment));
-
- Assertions.assertThatThrownBy(() -> commentService.updateComment(singleComment.getId(), request, noAuthInfo))
- .isInstanceOf(AuthorizationException.class);
- }
-
- @Test
- @DisplayName("권한이 있는 댓글을 삭제한다.")
- void deleteComment() {
- Mockito.when(commentRepository.findById(any(Long.class)))
- .thenReturn(Optional.ofNullable(singleComment));
-
- CommentDeleteResponse response = commentService.deleteComment(singleComment.getId(), authInfo);
-
- Assertions.assertThat(response.getComment_id()).isEqualTo(singleComment.getId());
- }
-
- @Test
- @DisplayName("권한이 없는 댓글은 삭제할 수 없다")
- void deleteComment_exception_noAuth() {
- Mockito.when(commentRepository.findById(any(Long.class)))
- .thenReturn(Optional.ofNullable(singleComment));
-
- AuthInfo noAuthInfo = AuthInfo.builder()
- .sub("no")
- .username("no")
- .build();
-
- Assertions.assertThatThrownBy(() -> commentService.deleteComment(singleComment.getId(), noAuthInfo))
- .isInstanceOf(AuthorizationException.class);
- }
-
- @Test
- @DisplayName("특정 댓글의 대댓글을 작성한다.")
- void addReply() {
- NewReplyRequest request = NewReplyRequest.builder()
- .content("reply content")
- .build();
-
- Mockito.when(commentRepository.findById(any(Long.class)))
- .thenReturn(Optional.ofNullable(singleComment));
-
- commentService.addReply(singleComment.getId(), request, authInfo);
- Comment reply = singleComment.getChildren().get(0);
-
- Assertions.assertThat(reply.getContent()).isEqualTo(request.getContent());
- Assertions.assertThat(reply.getParent()).isEqualTo(singleComment);
- }
-
- @Test
- @DisplayName("대댓글의 대댓글을 작성할 수 없다.")
- void addReply_exception_depth() {
- NewReplyRequest request = NewReplyRequest.builder()
- .content("reply content")
- .build();
-
- Mockito.when(commentRepository.findById(any(Long.class)))
- .thenReturn(Optional.ofNullable(reply));
-
- Assertions.assertThatThrownBy(() -> commentService.addReply(reply.getId(), request, authInfo))
- .isInstanceOf(ReplyDepthException.class);
- }
-
- @Test
- @DisplayName("권한이 있는 대댓글을 삭제한다.")
- void deleteReply() {
- Mockito.when(commentRepository.findById(any(Long.class)))
- .thenReturn(Optional.ofNullable(reply));
-
- CommentDeleteResponse response = commentService.deleteComment(reply.getId(), authInfo);
-
- Assertions.assertThat(response.getComment_id()).isEqualTo(reply.getId());
- Assertions.assertThat(singleComment.getChildren().size()).isEqualTo(0);
- }
-
- @Test
- @DisplayName("권한이 없는 대댓글을 삭제할 수 없다.")
- void deleteReply_exception_noAuth() {
- Mockito.when(commentRepository.findById(any(Long.class)))
- .thenReturn(Optional.ofNullable(reply));
-
- AuthInfo noAuthInfo = AuthInfo.builder()
- .sub("no")
- .username("no")
- .build();
-
- Assertions.assertThatThrownBy(() -> commentService.deleteComment(reply.getId(), noAuthInfo))
- .isInstanceOf(AuthorizationException.class);
- }
-
- @Test
- @DisplayName("부모 댓글을 삭제해도 대댓글은 남아있다.")
- void deleteComment_keepChildren() {
- Mockito.when(commentRepository.findById(any(Long.class)))
- .thenReturn(Optional.ofNullable(singleComment));
-
- commentService.deleteComment(singleComment.getId(), authInfo);
-
- Assertions.assertThat(singleComment.isSoftRemoved()).isEqualTo(true);
- Assertions.assertThat(singleComment.getChildren().size()).isEqualTo(1);
- }
-
- @Test
- @DisplayName("특정 대댓글 삭제 후, 삭제 예정으로 처리되고 대댓글이 없는 부모 댓글을 삭제한다.")
- void deletePrentAndReply() {
- singleComment.willBeDeleted();
- Mockito.when(commentRepository.findById(any(Long.class)))
- .thenReturn(Optional.ofNullable(reply));
-
- commentService.deleteComment(reply.getId(), authInfo);
-
- Assertions.assertThat(singleComment.canDelete()).isEqualTo(true);
- }
-
-}
\ No newline at end of file
diff --git a/src/test/java/org/wooriverygood/api/course/controller/CourseControllerTest.java b/src/test/java/org/wooriverygood/api/course/api/CourseApiTest.java
similarity index 72%
rename from src/test/java/org/wooriverygood/api/course/controller/CourseControllerTest.java
rename to src/test/java/org/wooriverygood/api/course/api/CourseApiTest.java
index 2d114cd..310b165 100644
--- a/src/test/java/org/wooriverygood/api/course/controller/CourseControllerTest.java
+++ b/src/test/java/org/wooriverygood/api/course/api/CourseApiTest.java
@@ -1,33 +1,35 @@
-package org.wooriverygood.api.course.controller;
+package org.wooriverygood.api.course.api;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
-import org.mockito.Mockito;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.wooriverygood.api.course.dto.CourseNameResponse;
import org.wooriverygood.api.course.dto.CourseResponse;
+import org.wooriverygood.api.course.dto.CoursesResponse;
import org.wooriverygood.api.course.dto.NewCourseRequest;
-import org.wooriverygood.api.util.ControllerTest;
+import org.wooriverygood.api.util.ApiTest;
import java.util.ArrayList;
import java.util.List;
-import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyLong;
+import static org.mockito.Mockito.when;
import static org.springframework.restdocs.mockmvc.MockMvcRestDocumentation.document;
-public class CourseControllerTest extends ControllerTest {
- List responses = new ArrayList<>();
+public class CourseApiTest extends ApiTest {
+
+ private List responses = new ArrayList<>();
@BeforeEach
void setUp() {
- for (int i = 0; i < 2; i++) {
+ for (long i = 0; i < 2; i++) {
responses.add(CourseResponse.builder()
- .course_id((long)i)
- .course_category("Zhuanye")
- .course_credit(5)
- .course_name("Gaoshu"+i)
+ .courseId(i)
+ .courseCategory("Zhuanye")
+ .courseCredit(5)
+ .courseName("Gaoshu"+i)
.isYouguan(0)
.kaikeYuanxi("Xinke")
.reviewCount(0)
@@ -38,7 +40,7 @@ void setUp() {
@Test
@DisplayName("모든 강의 조회 요청을 받으면 강의를 반환한다.")
void getCourses() {
- Mockito.when(courseService.findAll()).thenReturn(responses);
+ when(courseFindService.findAll()).thenReturn(new CoursesResponse(responses));
restDocs
.contentType(MediaType.APPLICATION_JSON_VALUE)
@@ -53,9 +55,9 @@ void getCourses() {
@DisplayName("새로운 수업을 성공적으로 등록한다.")
public void addCourse() {
NewCourseRequest request = NewCourseRequest.builder()
- .course_name("테스트 강의")
- .course_category("전공")
- .course_credit(5)
+ .courseName("테스트 강의")
+ .courseCategory("전공")
+ .courseCredit(5)
.kaikeYuanxi("씬커")
.isYouguan(0)
.build();
@@ -73,11 +75,9 @@ public void addCourse() {
@Test
@DisplayName("특정 강의의 이름을 반환한다.")
void getCourseName() {
- CourseNameResponse response = CourseNameResponse.builder()
- .course_name("테스트 강의")
- .build();
+ CourseNameResponse response = new CourseNameResponse("테스트 강의");
- Mockito.when(courseService.getCourseName(any(Long.class)))
+ when(courseFindService.findCourseName(anyLong()))
.thenReturn(response);
restDocs
@@ -89,4 +89,5 @@ void getCourseName() {
.apply(document("course/get/name/success"))
.statusCode(HttpStatus.OK.value());
}
+
}
diff --git a/src/test/java/org/wooriverygood/api/course/application/CourseCreateServiceTest.java b/src/test/java/org/wooriverygood/api/course/application/CourseCreateServiceTest.java
new file mode 100644
index 0000000..f8c0b2e
--- /dev/null
+++ b/src/test/java/org/wooriverygood/api/course/application/CourseCreateServiceTest.java
@@ -0,0 +1,40 @@
+package org.wooriverygood.api.course.application;
+
+import org.junit.jupiter.api.DisplayName;
+import org.junit.jupiter.api.Test;
+import org.mockito.InjectMocks;
+import org.mockito.Mock;
+import org.wooriverygood.api.course.domain.Course;
+import org.wooriverygood.api.course.dto.NewCourseRequest;
+import org.wooriverygood.api.course.repository.CourseRepository;
+import org.wooriverygood.api.util.MockTest;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.verify;
+
+public class CourseCreateServiceTest extends MockTest {
+
+ @InjectMocks
+ private CourseCreateService courseCreateService;
+
+ @Mock
+ private CourseRepository courseRepository;
+
+
+ @Test
+ @DisplayName("새로운 강의를 추가한다.")
+ void addCourse() {
+ NewCourseRequest request = NewCourseRequest.builder()
+ .courseName("테스트 강의")
+ .courseCategory("전공")
+ .courseCredit(5)
+ .kaikeYuanxi("씬커")
+ .isYouguan(0)
+ .build();
+
+ courseCreateService.addCourse(request);
+
+ verify(courseRepository).save(any(Course.class));
+ }
+
+}
diff --git a/src/test/java/org/wooriverygood/api/course/application/CourseFindServiceTest.java b/src/test/java/org/wooriverygood/api/course/application/CourseFindServiceTest.java
new file mode 100644
index 0000000..625fc07
--- /dev/null
+++ b/src/test/java/org/wooriverygood/api/course/application/CourseFindServiceTest.java
@@ -0,0 +1,81 @@
+package org.wooriverygood.api.course.application;
+
+import org.assertj.core.api.Assertions;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.DisplayName;
+import org.junit.jupiter.api.Test;
+import org.mockito.InjectMocks;
+import org.mockito.Mock;
+import org.wooriverygood.api.course.domain.Course;
+import org.wooriverygood.api.course.dto.CourseNameResponse;
+import org.wooriverygood.api.course.dto.CoursesResponse;
+import org.wooriverygood.api.course.repository.CourseRepository;
+import org.wooriverygood.api.util.MockTest;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Optional;
+
+import static org.mockito.ArgumentMatchers.anyLong;
+import static org.mockito.Mockito.when;
+
+class CourseFindServiceTest extends MockTest {
+
+ @InjectMocks
+ private CourseFindService courseFindService;
+
+ @Mock
+ private CourseRepository courseRepository;
+
+ private Course course = Course.builder()
+ .id(3L)
+ .name("Gaoshu Test")
+ .category("Zhuanye")
+ .credit(5)
+ .isYouguan(0)
+ .kaikeYuanxi("Xinke")
+ .reviewCount(0)
+ .build();
+
+ private List courses = new ArrayList<>();
+
+ private final int COURSES_COUNT = 10;
+
+
+ @BeforeEach
+ void setup() {
+ for (long i = 1; i <= COURSES_COUNT; i++) {
+ courses.add(Course.builder()
+ .id(i)
+ .name("Course" + i)
+ .category("Zhuanye")
+ .credit(5)
+ .isYouguan(0)
+ .kaikeYuanxi("Xinke")
+ .reviewCount(0)
+ .build());
+ }
+ }
+
+ @Test
+ @DisplayName("강의 전체 조회")
+ void getCourses() {
+ when(courseRepository.findAll()).thenReturn(courses);
+
+ CoursesResponse response = courseFindService.findAll();
+
+ Assertions.assertThat(response.getCourses().size()).isEqualTo(COURSES_COUNT);
+ }
+
+ @Test
+ @DisplayName("특정 강의의 이름을 조회온다.")
+ void getCourseName() {
+ when(courseRepository.findById(anyLong()))
+ .thenReturn(Optional.ofNullable(course));
+
+ CourseNameResponse response = courseFindService.findCourseName(3L);
+
+ Assertions.assertThat(response.getCourseName()).isEqualTo(course.getName());
+ }
+
+}
\ No newline at end of file
diff --git a/src/test/java/org/wooriverygood/api/course/service/CourseServiceTest.java b/src/test/java/org/wooriverygood/api/course/service/CourseServiceTest.java
deleted file mode 100644
index 6987151..0000000
--- a/src/test/java/org/wooriverygood/api/course/service/CourseServiceTest.java
+++ /dev/null
@@ -1,122 +0,0 @@
-package org.wooriverygood.api.course.service;
-
-import org.assertj.core.api.Assertions;
-import org.junit.jupiter.api.BeforeEach;
-import org.junit.jupiter.api.DisplayName;
-import org.junit.jupiter.api.Test;
-import org.junit.jupiter.api.extension.ExtendWith;
-import org.mockito.InjectMocks;
-import org.mockito.Mock;
-import org.mockito.Mockito;
-import org.mockito.junit.jupiter.MockitoExtension;
-import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.boot.test.context.SpringBootTest;
-import org.springframework.boot.test.mock.mockito.MockBean;
-import org.springframework.transaction.annotation.Transactional;
-import org.wooriverygood.api.course.domain.Courses;
-import org.wooriverygood.api.course.dto.CourseNameResponse;
-import org.wooriverygood.api.course.dto.CourseResponse;
-import org.wooriverygood.api.course.dto.NewCourseRequest;
-import org.wooriverygood.api.course.dto.NewCourseResponse;
-import org.wooriverygood.api.course.repository.CourseRepository;
-
-import javax.swing.text.html.Option;
-import java.util.ArrayList;
-import java.util.List;
-import java.util.Optional;
-
-import static org.mockito.Mockito.when;
-import static org.mockito.ArgumentMatchers.any;
-
-@ExtendWith(MockitoExtension.class)
-public class CourseServiceTest {
- @Mock
- private CourseRepository courseRepository;
- @InjectMocks
- private CourseService courseService;
-
- private List testCourses = new ArrayList<>();
-
- private final int COURSES_COUNT = 10;
-
- @BeforeEach
- void setup() {
- for(int i = 1; i<=COURSES_COUNT; i++) {
- testCourses.add(Courses.builder()
- .id((long)i)
- .course_name("Gaoshu" + i)
- .course_category("Zhuanye")
- .course_credit(5)
- .isYouguan(0)
- .kaikeYuanxi("Xinke")
- .reviewCount(0)
- .build());
- }
- }
-
- Courses singleCourse = Courses.builder()
- .id(3L)
- .course_name("Gaoshu Test")
- .course_category("Zhuanye")
- .course_credit(5)
- .isYouguan(0)
- .kaikeYuanxi("Xinke")
- .reviewCount(0)
- .build();
-
-
- @Test
- @DisplayName("강의 목록 조회")
- void getCourses() {
- Mockito.when(courseRepository.findAll()).thenReturn(testCourses);
-
- List responses = courseService.findAll();
-
- // Then
- Assertions.assertThat(responses.size()).isEqualTo(COURSES_COUNT);
-
- }
-
- @Test
- @DisplayName("새로운 강의를 추가한다.")
- void addCourse() {
- NewCourseRequest newCourseRequest = NewCourseRequest.builder()
- .course_name("테스트 강의")
- .course_category("전공")
- .course_credit(5)
- .kaikeYuanxi("씬커")
- .isYouguan(0)
- .build();
-
- Mockito.when(courseRepository.save(any(Courses.class)))
- .thenReturn(Courses.builder()
- .course_name(newCourseRequest.getCourse_name())
- .course_category(newCourseRequest.getCourse_category())
- .course_credit(newCourseRequest.getCourse_credit())
- .kaikeYuanxi(newCourseRequest.getKaikeYuanxi())
- .isYouguan(newCourseRequest.getIsYouguan())
- .reviewCount(0)
- .build());
-
- NewCourseResponse response = courseService.addCourse(newCourseRequest);
-
- Assertions.assertThat(response.getCourse_name()).isEqualTo(newCourseRequest.getCourse_name());
- Assertions.assertThat(response.getCourse_credit()).isEqualTo(newCourseRequest.getCourse_credit());
- Assertions.assertThat(response.getCourse_category()).isEqualTo(newCourseRequest.getCourse_category());
- Assertions.assertThat(response.getKaikeYuanxi()).isEqualTo(newCourseRequest.getKaikeYuanxi());
- Assertions.assertThat(response.getIsYouguan()).isEqualTo(newCourseRequest.getIsYouguan());
- }
-
- @Test
- @DisplayName("특정 강의의 이름을 받아온다.")
- void getCourseName() {
- Mockito.when(courseRepository.findById(any()))
- .thenReturn(Optional.ofNullable(singleCourse));
-
- CourseNameResponse response = courseService.getCourseName(3L);
-
- Assertions.assertThat(response.getCourse_name()).isEqualTo(singleCourse.getCourse_name());
-
- }
-
-}
diff --git a/src/test/java/org/wooriverygood/api/post/controller/PostControllerTest.java b/src/test/java/org/wooriverygood/api/post/api/PostApiTest.java
similarity index 64%
rename from src/test/java/org/wooriverygood/api/post/controller/PostControllerTest.java
rename to src/test/java/org/wooriverygood/api/post/api/PostApiTest.java
index 33d5942..a86351c 100644
--- a/src/test/java/org/wooriverygood/api/post/controller/PostControllerTest.java
+++ b/src/test/java/org/wooriverygood/api/post/api/PostApiTest.java
@@ -1,35 +1,38 @@
-package org.wooriverygood.api.post.controller;
+package org.wooriverygood.api.post.api;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
-import org.mockito.Mockito;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
-import org.wooriverygood.api.advice.exception.AuthorizationException;
-import org.wooriverygood.api.advice.exception.PostNotFoundException;
+import org.wooriverygood.api.global.error.exception.AuthorizationException;
+import org.wooriverygood.api.post.exception.PostNotFoundException;
import org.wooriverygood.api.post.dto.*;
-import org.wooriverygood.api.support.AuthInfo;
-import org.wooriverygood.api.util.ControllerTest;
+import org.wooriverygood.api.global.auth.AuthInfo;
+import org.wooriverygood.api.util.ApiTest;
import org.wooriverygood.api.util.ResponseFixture;
-import static org.mockito.ArgumentMatchers.any;
-import static org.springframework.restdocs.mockmvc.MockMvcRestDocumentation.document;
+import java.time.LocalDateTime;
+import java.util.ArrayList;
+import java.util.List;
-class PostControllerTest extends ControllerTest {
+import static org.mockito.ArgumentMatchers.*;
+import static org.mockito.Mockito.*;
+import static org.springframework.restdocs.mockmvc.MockMvcRestDocumentation.document;
+class PostApiTest extends ApiTest {
@Test
@DisplayName("전체 게시글의 1번째 페이지를 반환한다.")
void findPosts() {
- Mockito.when(postService.findPosts(any(AuthInfo.class), any(Pageable.class)))
+ when(postFindService.findPosts(any(AuthInfo.class), any(Pageable.class), anyString()))
.thenReturn(ResponseFixture.postsResponse(1, 39, testAuthInfo));
restDocs
.contentType(MediaType.APPLICATION_JSON_VALUE)
.header("Authorization", "Bearer aws-cognito-access-token")
- .when().get("/community?page=1")
+ .when().get("/posts?page=1")
.then().log().all()
.assertThat()
.apply(document("post/find/all/success"))
@@ -39,13 +42,35 @@ void findPosts() {
@Test
@DisplayName("자유 카테고리의 2번째 페이지를 반환한다.")
void findPostsByCategory() {
- Mockito.when(postService.findPostsByCategory(any(AuthInfo.class), any(Pageable.class), any(String.class)))
- .thenReturn(ResponseFixture.postsResponse(2, 39, "자유", testAuthInfo));
+ List responses = new ArrayList<>();
+ int bound = Math.min(39 - 2 * 10 + 1, 10);
+ int start = 2 * 10 + 1;
+ for (long i = start; i < start + bound; i++)
+ responses.add(PostResponse.builder()
+ .postId(i)
+ .postTitle("title_" + i)
+ .postCategory("자유")
+ .postComments((int) (Math.random() * 100))
+ .postLikes((int) (Math.random() * 100))
+ .postTime(LocalDateTime.now())
+ .liked(i % 6 == 0)
+ .updated(i % 9 == 0)
+ .reported(false)
+ .memberId(1L)
+ .isMine(false)
+ .build());
+
+ when(postFindService.findPosts(any(AuthInfo.class), any(Pageable.class), anyString()))
+ .thenReturn(PostsResponse.builder()
+ .posts(responses)
+ .totalPostCount(39)
+ .totalPageCount((int) Math.ceil((double) 39 / 10))
+ .build());
restDocs
.contentType(MediaType.APPLICATION_JSON_VALUE)
.header("Authorization", "Bearer aws-cognito-access-token")
- .when().get("/community/category/free")
+ .when().get("/posts?page=2&category=free")
.then().log().all()
.assertThat()
.apply(document("post/find/category/success"))
@@ -55,13 +80,25 @@ void findPostsByCategory() {
@Test
@DisplayName("특정 게시글 조회 요청을 받으면 게시글을 반환한다.")
void findPost() {
- Mockito.when(postService.findPostById(any(Long.class), any(AuthInfo.class)))
- .thenReturn(ResponseFixture.postResponse(4L, testAuthInfo));
+ when(postFindService.findPostById(anyLong(), any(AuthInfo.class)))
+ .thenReturn(PostDetailResponse.builder()
+ .postId(1L)
+ .postTitle("title_1")
+ .postCategory("자유")
+ .postComments((int) (Math.random() * 100))
+ .postLikes((int) (Math.random() * 100))
+ .postTime(LocalDateTime.now())
+ .liked(false)
+ .updated(false)
+ .reported(false)
+ .memberId(1L)
+ .isMine(false)
+ .build());
restDocs
.contentType(MediaType.APPLICATION_JSON_VALUE)
.header("Authorization", "Bearer aws-cognito-access-token")
- .when().get("/community/4")
+ .when().get("/posts/4")
.then().log().all()
.assertThat()
.apply(document("post/find/one/success"))
@@ -71,13 +108,13 @@ void findPost() {
@Test
@DisplayName("유효하지 않은 id로 게시글을 조회하면 404를 반환한다.")
void findPost_exception_invalidId() {
- Mockito.when(postService.findPostById(any(Long.class), any(AuthInfo.class)))
+ when(postFindService.findPostById(anyLong(), any(AuthInfo.class)))
.thenThrow(new PostNotFoundException());
restDocs
.contentType(MediaType.APPLICATION_JSON_VALUE)
.header("Authorization", "Bearer aws-cognito-access-token")
- .when().get("/community/1")
+ .when().get("/posts/1")
.then().log().all()
.assertThat()
.apply(document("post/find/one/fail"))
@@ -88,24 +125,16 @@ void findPost_exception_invalidId() {
@DisplayName("새로운 게시글을 성공적으로 등록한다.")
public void addPost() {
NewPostRequest request = NewPostRequest.builder()
- .post_title("title")
- .post_category("자유")
- .post_content("content")
+ .postTitle("title")
+ .postCategory("자유")
+ .postContent("content")
.build();
- Mockito.when(postService.addPost(any(AuthInfo.class), any(NewPostRequest.class)))
- .thenReturn(NewPostResponse.builder()
- .post_id(6L)
- .title(request.getPost_title())
- .category(request.getPost_category())
- .author(testAuthInfo.getUsername())
- .build());
-
restDocs
.contentType(MediaType.APPLICATION_JSON_VALUE)
.header("Authorization", "Bearer aws-cognito-access-token")
.body(request)
- .when().post("/community")
+ .when().post("/posts")
.then().log().all()
.assertThat()
.apply(document("post/create/success"))
@@ -116,15 +145,15 @@ public void addPost() {
@DisplayName("새로운 게시글의 제목이 없으면 400을 반환한다.")
public void addPost_exception_noTitle() {
NewPostRequest request = NewPostRequest.builder()
- .post_category("자유")
- .post_content("content")
+ .postCategory("자유")
+ .postContent("content")
.build();
restDocs
.contentType(MediaType.APPLICATION_JSON_VALUE)
.header("Authorization", "Bearer aws-cognito-access-token")
.body(request)
- .when().post("/community")
+ .when().post("/posts")
.then().log().all()
.assertThat()
.apply(document("post/create/fail/noTitle"))
@@ -134,13 +163,13 @@ public void addPost_exception_noTitle() {
@Test
@DisplayName("사용자 본인이 작성한 게시글을 불러온다.")
void findMyPosts() {
- Mockito.when(postService.findMyPosts(any(AuthInfo.class), any(PageRequest.class)))
+ when(postFindService.findMyPosts(any(AuthInfo.class), any(PageRequest.class)))
.thenReturn(ResponseFixture.postsResponse(1, 14, testAuthInfo));
restDocs
.contentType(MediaType.APPLICATION_JSON_VALUE)
.header("Authorization", "Bearer aws-cognito-access-token")
- .when().get("/community/me?page=1")
+ .when().get("/posts/me?page=1")
.then().log().all()
.assertThat()
.apply(document("post/find/me/success"))
@@ -150,16 +179,16 @@ void findMyPosts() {
@Test
@DisplayName("특정 게시글의 좋아요를 1 올리거나 내린다.")
void likePost() {
- Mockito.when(postService.likePost(any(Long.class), any(AuthInfo.class)))
+ when(postLikeToggleService.togglePostLike(anyLong(), any(AuthInfo.class)))
.thenReturn(PostLikeResponse.builder()
- .like_count(5)
+ .likeCount(5)
.liked(true)
.build());
restDocs
.contentType(MediaType.APPLICATION_JSON_VALUE)
.header("Authorization", "Bearer aws-cognito-access-token")
- .when().put("/community/1/like")
+ .when().put("/posts/1/like")
.then().log().all()
.assertThat()
.apply(document("post/like/success"))
@@ -170,42 +199,38 @@ void likePost() {
@DisplayName("권한이 있는 게시글을 수정한다.")
void updatePost() {
PostUpdateRequest request = PostUpdateRequest.builder()
- .post_title("new title")
- .post_content("new content")
+ .postTitle("new title")
+ .postContent("new content")
.build();
- Mockito.when(postService.updatePost(any(Long.class), any(PostUpdateRequest.class), any(AuthInfo.class)))
- .thenReturn(PostUpdateResponse.builder()
- .post_id((long) 1)
- .build());
-
restDocs
.contentType(MediaType.APPLICATION_JSON_VALUE)
.header("Authorization", "Bearer aws-cognito-access-token")
.body(request)
- .when().put("/community/1")
+ .when().put("/posts/1")
.then().log().all()
.assertThat()
.apply(document("post/update/success"))
- .statusCode(HttpStatus.OK.value());
+ .statusCode(HttpStatus.NO_CONTENT.value());
}
@Test
@DisplayName("권한이 없는 게시글을 수정하면 403을 반환한다.")
void updatePost_exception_noAuth() {
PostUpdateRequest request = PostUpdateRequest.builder()
- .post_title("new title")
- .post_content("new content")
+ .postTitle("new title")
+ .postContent("new content")
.build();
- Mockito.when(postService.updatePost(any(Long.class), any(PostUpdateRequest.class), any(AuthInfo.class)))
- .thenThrow(new AuthorizationException());
+ doThrow(new AuthorizationException())
+ .when(postUpdateService)
+ .updatePost(anyLong(), any(PostUpdateRequest.class), any(AuthInfo.class));
restDocs
.contentType(MediaType.APPLICATION_JSON_VALUE)
.header("Authorization", "Bearer aws-cognito-access-token")
.body(request)
- .when().put("/community/1")
+ .when().put("/posts/1")
.then().log().all()
.assertThat()
.apply(document("post/update/fail/noAuth"))
@@ -216,14 +241,14 @@ void updatePost_exception_noAuth() {
@Test
void updatePost_exception_noTitle() {
PostUpdateRequest postUpdateRequest = PostUpdateRequest.builder()
- .post_content("content")
+ .postContent("content")
.build();
restDocs
.contentType(MediaType.APPLICATION_JSON_VALUE)
.body(postUpdateRequest)
.header("Authorization", "any")
- .when().put("/community/1")
+ .when().put("/posts/1")
.then().log().all()
.assertThat()
.apply(document("post/update/fail/noTitle"))
@@ -233,32 +258,29 @@ void updatePost_exception_noTitle() {
@Test
@DisplayName("권한이 있는 게시글을 삭제한다.")
void deletePost() {
- Mockito.when(postService.deletePost(any(Long.class), any(AuthInfo.class)))
- .thenReturn(PostDeleteResponse.builder()
- .post_id(7L)
- .build());
-
restDocs
.header("Authorization", "any")
- .when().delete("/community/7")
+ .when().delete("/posts/7")
.then().log().all()
.assertThat()
.apply(document("post/delete/success"))
- .statusCode(HttpStatus.OK.value());
+ .statusCode(HttpStatus.NO_CONTENT.value());
}
@Test
@DisplayName("권한이 없는 게시글을 삭제하면 403을 반환한다.")
void deletePost_exception_forbidden() {
- Mockito.when(postService.deletePost(any(Long.class), any(AuthInfo.class)))
- .thenThrow(new AuthorizationException());
+ doThrow(new AuthorizationException())
+ .when(postDeleteService)
+ .deletePost(any(AuthInfo.class), anyLong());
restDocs
.header("Authorization", "any")
- .when().delete("/community/7")
+ .when().delete("/posts/7")
.then().log().all()
.assertThat()
.apply(document("post/delete/fail/noAuth"))
.statusCode(HttpStatus.FORBIDDEN.value());
}
+
}
\ No newline at end of file
diff --git a/src/test/java/org/wooriverygood/api/post/application/PostCreateServiceTest.java b/src/test/java/org/wooriverygood/api/post/application/PostCreateServiceTest.java
new file mode 100644
index 0000000..ca0e67e
--- /dev/null
+++ b/src/test/java/org/wooriverygood/api/post/application/PostCreateServiceTest.java
@@ -0,0 +1,67 @@
+package org.wooriverygood.api.post.application;
+
+import org.junit.jupiter.api.DisplayName;
+import org.junit.jupiter.api.Test;
+import org.mockito.InjectMocks;
+import org.mockito.Mock;
+import org.wooriverygood.api.member.domain.Member;
+import org.wooriverygood.api.member.repository.MemberRepository;
+import org.wooriverygood.api.post.domain.Post;
+import org.wooriverygood.api.post.dto.NewPostRequest;
+import org.wooriverygood.api.post.exception.InvalidPostCategoryException;
+import org.wooriverygood.api.post.repository.PostRepository;
+import org.wooriverygood.api.util.MockTest;
+
+import java.util.Optional;
+
+import static org.assertj.core.api.Assertions.assertThatThrownBy;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyLong;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+class PostCreateServiceTest extends MockTest {
+
+ @InjectMocks
+ private PostCreateService postCreateService;
+
+ @Mock
+ private PostRepository postRepository;
+
+ @Mock
+ private MemberRepository memberRepository;
+
+
+ @Test
+ @DisplayName("새로운 게시글을 작성한다.")
+ void addPost() {
+ NewPostRequest request = NewPostRequest.builder()
+ .postTitle("title")
+ .postCategory("자유")
+ .postContent("content")
+ .build();
+ when(memberRepository.findById(anyLong()))
+ .thenReturn(Optional.ofNullable(member));
+
+ postCreateService.addPost(authInfo, request);
+
+ verify(postRepository).save(any(Post.class));
+ }
+
+ @Test
+ @DisplayName("새로운 게시글의 카테고리가 유효하지 않으면 등록에 실패한다.")
+ void addPost_exception_invalid_category() {
+ NewPostRequest newPostRequest = NewPostRequest.builder()
+ .postTitle("title")
+ .postCategory("자유유")
+ .postContent("content")
+ .build();
+
+ when(memberRepository.findById(anyLong()))
+ .thenReturn(Optional.ofNullable(member));
+
+ assertThatThrownBy(() -> postCreateService.addPost(authInfo, newPostRequest))
+ .isInstanceOf(InvalidPostCategoryException.class);
+ }
+
+}
\ No newline at end of file
diff --git a/src/test/java/org/wooriverygood/api/post/application/PostDeleteServiceTest.java b/src/test/java/org/wooriverygood/api/post/application/PostDeleteServiceTest.java
new file mode 100644
index 0000000..bb3a8c0
--- /dev/null
+++ b/src/test/java/org/wooriverygood/api/post/application/PostDeleteServiceTest.java
@@ -0,0 +1,76 @@
+package org.wooriverygood.api.post.application;
+
+import org.junit.jupiter.api.DisplayName;
+import org.junit.jupiter.api.Test;
+import org.mockito.InjectMocks;
+import org.mockito.Mock;
+import org.wooriverygood.api.global.error.exception.AuthorizationException;
+import org.wooriverygood.api.comment.repository.CommentRepository;
+import org.wooriverygood.api.member.domain.Member;
+import org.wooriverygood.api.member.repository.MemberRepository;
+import org.wooriverygood.api.post.domain.Post;
+import org.wooriverygood.api.post.repository.PostLikeRepository;
+import org.wooriverygood.api.post.repository.PostRepository;
+
+import java.util.Optional;
+
+import static org.assertj.core.api.Assertions.assertThatThrownBy;
+import static org.junit.jupiter.api.Assertions.assertAll;
+import static org.mockito.ArgumentMatchers.anyLong;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+class PostDeleteServiceTest extends PostServiceTest {
+
+ @InjectMocks
+ private PostDeleteService postDeleteService;
+
+ @Mock
+ private PostRepository postRepository;
+
+ @Mock
+ private CommentRepository commentRepository;
+
+ @Mock
+ private PostLikeRepository postLikeRepository;
+
+ @Mock
+ private MemberRepository memberRepository;
+
+
+ @Test
+ @DisplayName("권한이 있는 게시글을 삭제한다.")
+ void deletePost() {
+ when(memberRepository.findById(anyLong()))
+ .thenReturn(Optional.ofNullable(member));
+ when(postRepository.findById(anyLong()))
+ .thenReturn(Optional.ofNullable(post));
+
+ postDeleteService.deletePost(authInfo, post.getId());
+
+ assertAll(
+ () -> verify(commentRepository).deleteAllByPost(post),
+ () -> verify(postLikeRepository).deleteAllByPost(post)
+ );
+ }
+
+ @Test
+ @DisplayName("권한이 없는 게시글을 삭제하면 예외가 발생한다.")
+ void deletePost_exception_noAuth() {
+ Post noAuthPost = Post.builder()
+ .id(9L)
+ .title("title")
+ .content("content")
+ .member(new Member(5L, "username"))
+ .build();
+
+ when(memberRepository.findById(anyLong()))
+ .thenReturn(Optional.ofNullable(member));
+ when(postRepository.findById(anyLong()))
+ .thenReturn(Optional.ofNullable(noAuthPost));
+
+ assertThatThrownBy(() -> postDeleteService.deletePost(authInfo, post.getId()))
+ .isInstanceOf(AuthorizationException.class);
+ }
+
+}
\ No newline at end of file
diff --git a/src/test/java/org/wooriverygood/api/post/application/PostFindServiceTest.java b/src/test/java/org/wooriverygood/api/post/application/PostFindServiceTest.java
new file mode 100644
index 0000000..d557dcc
--- /dev/null
+++ b/src/test/java/org/wooriverygood/api/post/application/PostFindServiceTest.java
@@ -0,0 +1,171 @@
+package org.wooriverygood.api.post.application;
+
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.DisplayName;
+import org.junit.jupiter.api.Test;
+import org.mockito.InjectMocks;
+import org.mockito.Mock;
+import org.springframework.data.domain.PageImpl;
+import org.springframework.data.domain.PageRequest;
+import org.springframework.data.domain.Pageable;
+import org.wooriverygood.api.member.domain.Member;
+import org.wooriverygood.api.member.repository.MemberRepository;
+import org.wooriverygood.api.post.domain.Post;
+import org.wooriverygood.api.post.domain.PostCategory;
+import org.wooriverygood.api.post.dto.PostDetailResponse;
+import org.wooriverygood.api.post.dto.PostsResponse;
+import org.wooriverygood.api.post.exception.PostNotFoundException;
+import org.wooriverygood.api.post.repository.PostLikeRepository;
+import org.wooriverygood.api.post.repository.PostRepository;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Optional;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.assertj.core.api.Assertions.assertThatThrownBy;
+import static org.junit.jupiter.api.Assertions.assertAll;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyLong;
+import static org.mockito.Mockito.when;
+
+class PostFindServiceTest extends PostServiceTest {
+
+ @InjectMocks
+ private PostFindService postFindService;
+
+ @Mock
+ private PostRepository postRepository;
+
+ @Mock
+ private PostLikeRepository postLikeRepository;
+
+ @Mock
+ private MemberRepository memberRepository;
+
+ private List posts = new ArrayList<>();
+
+ private List myPosts = new ArrayList<>();
+
+ private List freePosts = new ArrayList<>();
+
+ private final int POST_COUNT = 23;
+
+
+ @BeforeEach
+ void setUp() {
+ posts.clear();
+ myPosts.clear();
+ freePosts.clear();
+ for (long i = 1; i <= POST_COUNT; i++) {
+ Post post = Post.builder()
+ .id(i)
+ .category(PostCategory.FREE)
+ .title("title" + i)
+ .content("content" + i)
+ .member(member)
+ .comments(new ArrayList<>())
+ .postLikes(new ArrayList<>())
+ .build();
+ posts.add(post);
+ if (i > 9) {
+ myPosts.add(post);
+ freePosts.add(post);
+ }
+ }
+ }
+
+ @Test
+ @DisplayName("로그인 한 상황에서 모든 카테고리의 게시글을 불러온다.")
+ void findPosts_login() {
+ Pageable pageable = PageRequest.of(0, 10);
+
+ int start = (int) pageable.getOffset();
+ int end = Math.min(start + pageable.getPageSize(), this.posts.size());
+ List posts = this.posts.subList(start, end);
+ PageImpl page = new PageImpl<>(posts, pageable, this.posts.size());
+ when(postRepository.findAllByOrderByIdDesc(any(PageRequest.class)))
+ .thenReturn(page);
+ when(memberRepository.findById(anyLong()))
+ .thenReturn(Optional.ofNullable(member));
+ when(postLikeRepository.existsByPostAndMember(any(Post.class), any(Member.class)))
+ .thenReturn(true);
+
+ PostsResponse response = postFindService.findPosts(authInfo, pageable, "");
+
+ assertAll(
+ () -> assertThat(response.getPosts().size()).isEqualTo(10),
+ () -> assertThat(response.getTotalPageCount()).isEqualTo(3),
+ () -> assertThat(response.getTotalPostCount()).isEqualTo(POST_COUNT)
+ );
+ }
+
+ @Test
+ @DisplayName("자유 카테고리의 게시글을 불러온다.")
+ void findPosts_category_free() {
+ Pageable pageable = PageRequest.of(0, 10);
+ int start = (int) pageable.getOffset();
+ int end = Math.min(start + pageable.getPageSize(), this.freePosts.size());
+ List posts = this.freePosts.subList(start, end);
+ PageImpl page = new PageImpl<>(posts, pageable, this.freePosts.size());
+ when(postRepository.findAllByCategoryOrderByIdDesc(any(PostCategory.class), any(PageRequest.class)))
+ .thenReturn(page);
+ when(memberRepository.findById(anyLong()))
+ .thenReturn(Optional.ofNullable(member));
+
+ PostsResponse response = postFindService.findPosts(authInfo, pageable, "자유");
+
+ assertAll(
+ () -> assertThat(response.getPosts().size()).isEqualTo(10),
+ () -> assertThat(response.getTotalPageCount()).isEqualTo(2),
+ () -> assertThat(response.getTotalPostCount()).isEqualTo(14)
+ );
+ }
+
+ @Test
+ @DisplayName("유효한 id를 이용하여 특정 게시글을 불러온다.")
+ void findPostById() {
+ when(postRepository.findById(anyLong()))
+ .thenReturn(Optional.ofNullable(post));
+ when(memberRepository.findById(anyLong()))
+ .thenReturn(Optional.ofNullable(member));
+
+ PostDetailResponse response = postFindService.findPostById(6L, authInfo);
+
+ assertThat(response.getPostId()).isEqualTo(post.getId());
+ }
+
+ @Test
+ @DisplayName("유효하지 않은 id를 이용하여 특정 게시글을 불러오면 에러를 반환한다.")
+ void findPostById_exception_invalidId() {
+ when(postRepository.findById(any()))
+ .thenThrow(PostNotFoundException.class);
+
+ assertThatThrownBy(() -> postFindService.findPostById(6L, authInfo))
+ .isInstanceOf(PostNotFoundException.class);
+ }
+
+
+ @Test
+ @DisplayName("사용자 본인이 작성한 게시글을 불러온다.")
+ void findMyPosts() {
+ Pageable pageable = PageRequest.of(0, 10);
+ int start = (int) pageable.getOffset();
+ int end = Math.min(start + pageable.getPageSize(), this.myPosts.size());
+ List posts = this.myPosts.subList(start, end);
+ PageImpl page = new PageImpl<>(posts, pageable, this.myPosts.size());
+ when(postRepository.findByMemberOrderByIdDesc(any(Member.class), any(PageRequest.class)))
+ .thenReturn(page);
+ when(memberRepository.findById(anyLong()))
+ .thenReturn(Optional.ofNullable(member));
+
+ PostsResponse response = postFindService.findMyPosts(authInfo, pageable);
+
+ assertAll(
+ () -> assertThat(response.getPosts().size()).isEqualTo(10),
+ () -> assertThat(response.getTotalPageCount()).isEqualTo(2),
+ () -> assertThat(response.getTotalPostCount()).isEqualTo(14)
+ );
+ }
+
+}
\ No newline at end of file
diff --git a/src/test/java/org/wooriverygood/api/post/application/PostLikeToggleServiceTest.java b/src/test/java/org/wooriverygood/api/post/application/PostLikeToggleServiceTest.java
new file mode 100644
index 0000000..c4722e6
--- /dev/null
+++ b/src/test/java/org/wooriverygood/api/post/application/PostLikeToggleServiceTest.java
@@ -0,0 +1,78 @@
+package org.wooriverygood.api.post.application;
+
+import org.junit.jupiter.api.DisplayName;
+import org.junit.jupiter.api.Test;
+import org.mockito.InjectMocks;
+import org.mockito.Mock;
+import org.wooriverygood.api.member.domain.Member;
+import org.wooriverygood.api.member.repository.MemberRepository;
+import org.wooriverygood.api.post.domain.Post;
+import org.wooriverygood.api.post.domain.PostLike;
+import org.wooriverygood.api.post.dto.PostLikeResponse;
+import org.wooriverygood.api.post.repository.PostLikeRepository;
+import org.wooriverygood.api.post.repository.PostRepository;
+
+import java.util.Optional;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.junit.jupiter.api.Assertions.assertAll;
+import static org.mockito.ArgumentMatchers.*;
+import static org.mockito.Mockito.when;
+
+class PostLikeToggleServiceTest extends PostServiceTest {
+
+ @InjectMocks
+ private PostLikeToggleService postLikeToggleService;
+
+ @Mock
+ private PostRepository postRepository;
+
+ @Mock
+ private PostLikeRepository postLikeRepository;
+
+ @Mock
+ private MemberRepository memberRepository;
+
+
+ @Test
+ @DisplayName("특정 게시글에 좋아요를 1 올린다.")
+ void likePost_up() {
+ when(memberRepository.findById(anyLong()))
+ .thenReturn(Optional.ofNullable(member));
+ when(postRepository.findById(anyLong()))
+ .thenReturn(Optional.ofNullable(post));
+ when(postLikeRepository.findByPostAndMember(any(Post.class), any(Member.class)))
+ .thenReturn(Optional.empty());
+
+ PostLikeResponse response = postLikeToggleService.togglePostLike(post.getId(), authInfo);
+
+ assertAll(
+ () -> assertThat(response.getLikeCount()).isEqualTo(post.getLikeCount() + 1),
+ () -> assertThat(response.isLiked()).isEqualTo(true)
+ );
+ }
+
+ @Test
+ @DisplayName("특정 게시글에 좋아요를 1 내린다.")
+ void likePost_down() {
+ PostLike postLike = PostLike.builder()
+ .id(1L)
+ .post(post)
+ .member(member)
+ .build();
+ when(memberRepository.findById(anyLong()))
+ .thenReturn(Optional.ofNullable(member));
+ when(postRepository.findById(anyLong()))
+ .thenReturn(Optional.ofNullable(post));
+ when(postLikeRepository.findByPostAndMember(any(Post.class), any(Member.class)))
+ .thenReturn(Optional.ofNullable(postLike));
+
+ PostLikeResponse response = postLikeToggleService.togglePostLike(postLike.getId(), authInfo);
+
+ assertAll(
+ () -> assertThat(response.getLikeCount()).isEqualTo(post.getLikeCount() - 1),
+ () -> assertThat(response.isLiked()).isEqualTo(false)
+ );
+ }
+
+}
\ No newline at end of file
diff --git a/src/test/java/org/wooriverygood/api/post/application/PostServiceTest.java b/src/test/java/org/wooriverygood/api/post/application/PostServiceTest.java
new file mode 100644
index 0000000..6b944df
--- /dev/null
+++ b/src/test/java/org/wooriverygood/api/post/application/PostServiceTest.java
@@ -0,0 +1,28 @@
+package org.wooriverygood.api.post.application;
+
+import org.junit.jupiter.api.BeforeEach;
+import org.wooriverygood.api.post.domain.Post;
+import org.wooriverygood.api.post.domain.PostCategory;
+import org.wooriverygood.api.util.MockTest;
+
+import java.util.ArrayList;
+
+public class PostServiceTest extends MockTest {
+
+ protected Post post;
+
+ @BeforeEach
+ void dateSetUp() {
+ post = Post.builder()
+ .id(1L)
+ .category(PostCategory.OFFER)
+ .title("simple title")
+ .content("simple content")
+ .member(member)
+ .comments(new ArrayList<>())
+ .postLikes(new ArrayList<>())
+ .reports(new ArrayList<>())
+ .build();
+ }
+
+}
diff --git a/src/test/java/org/wooriverygood/api/post/application/PostUpdateServiceTest.java b/src/test/java/org/wooriverygood/api/post/application/PostUpdateServiceTest.java
new file mode 100644
index 0000000..22a5b59
--- /dev/null
+++ b/src/test/java/org/wooriverygood/api/post/application/PostUpdateServiceTest.java
@@ -0,0 +1,82 @@
+package org.wooriverygood.api.post.application;
+
+import org.junit.jupiter.api.DisplayName;
+import org.junit.jupiter.api.Test;
+import org.mockito.InjectMocks;
+import org.mockito.Mock;
+import org.wooriverygood.api.global.error.exception.AuthorizationException;
+import org.wooriverygood.api.member.domain.Member;
+import org.wooriverygood.api.member.repository.MemberRepository;
+import org.wooriverygood.api.post.domain.Post;
+import org.wooriverygood.api.post.domain.PostCategory;
+import org.wooriverygood.api.post.dto.PostUpdateRequest;
+import org.wooriverygood.api.post.repository.PostRepository;
+
+import java.util.ArrayList;
+import java.util.Optional;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.assertj.core.api.Assertions.assertThatThrownBy;
+import static org.junit.jupiter.api.Assertions.assertAll;
+import static org.mockito.ArgumentMatchers.anyLong;
+import static org.mockito.Mockito.when;
+
+class PostUpdateServiceTest extends PostServiceTest {
+
+ @InjectMocks
+ private PostUpdateService postUpdateService;
+
+ @Mock
+ private PostRepository postRepository;
+
+ @Mock
+ private MemberRepository memberRepository;
+
+ private Post noAuthPost = Post.builder()
+ .id(99L)
+ .category(PostCategory.OFFER)
+ .title("title99")
+ .content("content99")
+ .member(new Member(5L, "username"))
+ .comments(new ArrayList<>())
+ .postLikes(new ArrayList<>())
+ .build();
+
+
+ @Test
+ @DisplayName("권한이 있는 게시글을 수정한다.")
+ void updatePost() {
+ PostUpdateRequest request = PostUpdateRequest.builder()
+ .postTitle("new title")
+ .postContent("new content")
+ .build();
+ when(memberRepository.findById(anyLong()))
+ .thenReturn(Optional.ofNullable(member));
+ when(postRepository.findById(anyLong()))
+ .thenReturn(Optional.ofNullable(post));
+
+ postUpdateService.updatePost(post.getId(), request, authInfo);
+
+ assertAll(
+ () -> assertThat(post.getTitle()).isEqualTo(request.getPostTitle()),
+ () -> assertThat(post.getContent()).isEqualTo(request.getPostContent())
+ );
+ }
+
+ @Test
+ @DisplayName("권한이 없는 게시글을 수정할 수 없다.")
+ void updatePost_exception_noAuth() {
+ PostUpdateRequest request = PostUpdateRequest.builder()
+ .postTitle("new title")
+ .postContent("new content")
+ .build();
+ when(memberRepository.findById(anyLong()))
+ .thenReturn(Optional.ofNullable(member));
+ when(postRepository.findById(anyLong()))
+ .thenReturn(Optional.ofNullable(noAuthPost));
+
+ assertThatThrownBy(() -> postUpdateService.updatePost(noAuthPost.getId(), request, authInfo))
+ .isInstanceOf(AuthorizationException.class);
+ }
+
+}
\ No newline at end of file
diff --git a/src/test/java/org/wooriverygood/api/post/service/PostServiceTest.java b/src/test/java/org/wooriverygood/api/post/service/PostServiceTest.java
deleted file mode 100644
index 4a3144f..0000000
--- a/src/test/java/org/wooriverygood/api/post/service/PostServiceTest.java
+++ /dev/null
@@ -1,297 +0,0 @@
-package org.wooriverygood.api.post.service;
-
-import org.assertj.core.api.Assertions;
-import org.junit.jupiter.api.BeforeEach;
-import org.junit.jupiter.api.DisplayName;
-import org.junit.jupiter.api.Test;
-import org.junit.jupiter.api.extension.ExtendWith;
-import org.mockito.InjectMocks;
-import org.mockito.Mock;
-import org.mockito.Mockito;
-import org.mockito.junit.jupiter.MockitoExtension;
-import org.springframework.data.domain.PageImpl;
-import org.springframework.data.domain.PageRequest;
-import org.springframework.data.domain.Pageable;
-import org.wooriverygood.api.advice.exception.AuthorizationException;
-import org.wooriverygood.api.advice.exception.InvalidPostCategoryException;
-import org.wooriverygood.api.advice.exception.PostNotFoundException;
-import org.wooriverygood.api.comment.repository.CommentRepository;
-import org.wooriverygood.api.post.domain.Post;
-import org.wooriverygood.api.post.domain.PostCategory;
-import org.wooriverygood.api.post.domain.PostLike;
-import org.wooriverygood.api.post.dto.*;
-import org.wooriverygood.api.post.repository.PostLikeRepository;
-import org.wooriverygood.api.post.repository.PostRepository;
-import org.wooriverygood.api.support.AuthInfo;
-
-import java.util.ArrayList;
-import java.util.List;
-import java.util.Optional;
-
-import static org.mockito.ArgumentMatchers.any;
-
-@ExtendWith(MockitoExtension.class)
-class PostServiceTest {
-
- @InjectMocks
- private PostService postService;
-
- @Mock
- private PostRepository postRepository;
-
- @Mock
- private PostLikeRepository postLikeRepository;
-
- @Mock
- private CommentRepository commentRepository;
-
- private final int POST_COUNT = 23;
-
- List posts = new ArrayList<>();
-
- List myPosts = new ArrayList<>();
-
- List freePosts = new ArrayList<>();
-
- AuthInfo authInfo = AuthInfo.builder()
- .sub("22222-34534-123")
- .username("22222-34534-123")
- .build();
-
- Post singlePost = Post.builder()
- .id(6L)
- .category(PostCategory.OFFER)
- .title("title6")
- .content("content6")
- .author(authInfo.getUsername())
- .comments(new ArrayList<>())
- .postLikes(new ArrayList<>())
- .build();
-
- Post noAuthPost = Post.builder()
- .id(99L)
- .category(PostCategory.OFFER)
- .title("title99")
- .content("content99")
- .author("43434-45654-234")
- .comments(new ArrayList<>())
- .postLikes(new ArrayList<>())
- .build();
-
-
- @BeforeEach
- void setUpPosts() {
- for (int i = 1; i <= POST_COUNT; i++) {
- Post post = Post.builder()
- .id((long) i)
- .category(PostCategory.FREE)
- .title("title" + i)
- .content("content" + i)
- .author(authInfo.getUsername())
- .comments(new ArrayList<>())
- .postLikes(new ArrayList<>())
- .build();
- posts.add(post);
- if (post.getId() > 9) myPosts.add(post);
- if (post.getId() > 9) freePosts.add(post);
- }
- }
-
- @Test
- @DisplayName("로그인 한 상황에서 게시글을 불러온다.")
- void findPosts_login() {
- Pageable pageable = PageRequest.of(0, 10);
-
- int start = (int) pageable.getOffset();
- int end = Math.min(start + pageable.getPageSize(), this.posts.size());
- List posts = this.posts.subList(start, end);
- PageImpl page = new PageImpl<>(posts, pageable, this.posts.size());
- Mockito.when(postRepository.findAllByOrderByIdDesc(any(PageRequest.class)))
- .thenReturn(page);
-
- PostsResponse response = postService.findPosts(authInfo, pageable);
-
- Assertions.assertThat(response.getPosts().size()).isEqualTo(10);
- Assertions.assertThat(response.getTotalPageCount()).isEqualTo(3);
- Assertions.assertThat(response.getTotalPostCount()).isEqualTo(POST_COUNT);
- }
-
- @Test
- @DisplayName("자유 카테고리의 게시글을 불러온다.")
- void findPosts_category_free() {
- Pageable pageable = PageRequest.of(0, 10);
- int start = (int) pageable.getOffset();
- int end = Math.min(start + pageable.getPageSize(), this.freePosts.size());
- List posts = this.freePosts.subList(start, end);
- PageImpl page = new PageImpl<>(posts, pageable, this.freePosts.size());
- Mockito.when(postRepository.findAllByCategoryOrderByIdDesc(any(PostCategory.class), any(PageRequest.class)))
- .thenReturn(page);
-
- PostsResponse response = postService.findPostsByCategory(authInfo, pageable, "자유");
-
- Assertions.assertThat(response.getPosts().size()).isEqualTo(10);
- Assertions.assertThat(response.getTotalPageCount()).isEqualTo(2);
- Assertions.assertThat(response.getTotalPostCount()).isEqualTo(14);
- }
-
- @Test
- @DisplayName("유효한 id를 이용하여 특정 게시글을 불러온다.")
- void findPostById() {
- Mockito.when(postRepository.findById(any()))
- .thenReturn(Optional.ofNullable(singlePost));
-
- PostResponse response = postService.findPostById(6L, authInfo);
-
- Assertions.assertThat(response).isNotNull();
- }
-
- @Test
- @DisplayName("유효하지 않은 id를 이용하여 특정 게시글을 불러오면 에러를 반환한다.")
- void findPostById_exception_invalidId() {
- Mockito.when(postRepository.findById(any()))
- .thenThrow(PostNotFoundException.class);
-
- Assertions.assertThatThrownBy(() -> postService.findPostById(6L, authInfo))
- .isInstanceOf(PostNotFoundException.class);
- }
-
- @Test
- @DisplayName("새로운 게시글을 작성한다.")
- void addPost() {
- NewPostRequest newPostRequest = NewPostRequest.builder()
- .post_title("title")
- .post_category("자유")
- .post_content("content")
- .build();
-
- Mockito.when(postRepository.save(any(Post.class)))
- .thenReturn(Post.builder()
- .title(newPostRequest.getPost_title())
- .category(PostCategory.parse(newPostRequest.getPost_category()))
- .content(newPostRequest.getPost_content())
- .author(authInfo.getUsername())
- .build());
-
- NewPostResponse response = postService.addPost(authInfo, newPostRequest);
-
- Assertions.assertThat(response.getTitle()).isEqualTo(newPostRequest.getPost_title());
- Assertions.assertThat(response.getCategory()).isEqualTo(newPostRequest.getPost_category());
- Assertions.assertThat(response.getAuthor()).isEqualTo(authInfo.getUsername());
- }
-
- @Test
- @DisplayName("새로운 게시글의 카테고리가 유효하지 않으면 등록에 실패한다.")
- void addPost_exception_invalid_category() {
- NewPostRequest newPostRequest = NewPostRequest.builder()
- .post_title("title")
- .post_category("자유유")
- .post_content("content")
- .build();
-
- Assertions.assertThatThrownBy(() -> postService.addPost(authInfo, newPostRequest))
- .isInstanceOf(InvalidPostCategoryException.class);
- }
-
- @Test
- @DisplayName("사용자 본인이 작성한 게시글을 불러온다.")
- void findMyPosts() {
- Pageable pageable = PageRequest.of(0, 10);
- int start = (int) pageable.getOffset();
- int end = Math.min(start + pageable.getPageSize(), this.myPosts.size());
- List posts = this.myPosts.subList(start, end);
- PageImpl page = new PageImpl<>(posts, pageable, this.myPosts.size());
- Mockito.when(postRepository.findByAuthorOrderByIdDesc(any(String.class), any(PageRequest.class)))
- .thenReturn(page);
-
- PostsResponse response = postService.findMyPosts(authInfo, pageable);
-
- Assertions.assertThat(response.getPosts().size()).isEqualTo(10);
- Assertions.assertThat(response.getTotalPageCount()).isEqualTo(2);
- Assertions.assertThat(response.getTotalPostCount()).isEqualTo(14);
- }
-
- @Test
- @DisplayName("특정 게시글에 좋아요를 1 올린다.")
- void likePost_up() {
- Mockito.when(postRepository.findById(any(Long.class)))
- .thenReturn(Optional.ofNullable(singlePost));
-
- PostLikeResponse response = postService.likePost(singlePost.getId(), authInfo);
-
- Assertions.assertThat(response.getLike_count()).isEqualTo(singlePost.getLikeCount() + 1);
- Assertions.assertThat(response.isLiked()).isEqualTo(true);
- }
-
- @Test
- @DisplayName("특정 게시글에 좋아요를 1 내린다.")
- void likePost_down() {
- PostLike postLike = PostLike.builder()
- .id(1L)
- .post(singlePost)
- .username(authInfo.getUsername())
- .build();
- Mockito.when(postRepository.findById(any(Long.class)))
- .thenReturn(Optional.ofNullable(singlePost));
- Mockito.when(postLikeRepository.findByPostAndUsername(any(Post.class), any(String.class)))
- .thenReturn(Optional.ofNullable(postLike));
-
- PostLikeResponse response = postService.likePost(singlePost.getId(), authInfo);
-
- Assertions.assertThat(response.getLike_count()).isEqualTo(singlePost.getLikeCount() - 1);
- Assertions.assertThat(response.isLiked()).isEqualTo(false);
- }
-
- @Test
- @DisplayName("권한이 있는 게시글을 수정한다.")
- void updatePost() {
- PostUpdateRequest request = PostUpdateRequest.builder()
- .post_title("new title")
- .post_content("new content")
- .build();
-
- Mockito.when(postRepository.findById(any(Long.class)))
- .thenReturn(Optional.ofNullable(singlePost));
-
- PostUpdateResponse response = postService.updatePost(singlePost.getId(), request, authInfo);
-
- Assertions.assertThat(response.getPost_id()).isEqualTo(singlePost.getId());
- Assertions.assertThat(singlePost.isUpdated()).isEqualTo(true);
- }
-
- @Test
- @DisplayName("권한이 없는 게시글을 수정할 수 없다.")
- void updatePost_exception_noAuth() {
- PostUpdateRequest request = PostUpdateRequest.builder()
- .post_title("new title")
- .post_content("new content")
- .build();
-
- Mockito.when(postRepository.findById(any(Long.class)))
- .thenReturn(Optional.ofNullable(noAuthPost));
-
- Assertions.assertThatThrownBy(() -> postService.updatePost(noAuthPost.getId(), request, authInfo))
- .isInstanceOf(AuthorizationException.class);
- }
-
- @Test
- @DisplayName("권한이 있는 게시글을 삭제한다.")
- void deletePost() {
- Mockito.when(postRepository.findById(any(Long.class)))
- .thenReturn(Optional.ofNullable(singlePost));
-
- PostDeleteResponse response = postService.deletePost(singlePost.getId(), authInfo);
-
- Assertions.assertThat(response.getPost_id()).isEqualTo(singlePost.getId());
- }
-
- @Test
- @DisplayName("권한이 없는 게시글을 삭제한다.")
- void deletePost_exception_noAuth() {
- Mockito.when(postRepository.findById(any(Long.class)))
- .thenReturn(Optional.ofNullable(noAuthPost));
-
- Assertions.assertThatThrownBy(() -> postService.deletePost(noAuthPost.getId(), authInfo))
- .isInstanceOf(AuthorizationException.class);
- }
-
-}
\ No newline at end of file
diff --git a/src/test/java/org/wooriverygood/api/report/controller/ReportControllerTest.java b/src/test/java/org/wooriverygood/api/report/api/ReportApiTest.java
similarity index 62%
rename from src/test/java/org/wooriverygood/api/report/controller/ReportControllerTest.java
rename to src/test/java/org/wooriverygood/api/report/api/ReportApiTest.java
index a70db4b..9e1067d 100644
--- a/src/test/java/org/wooriverygood/api/report/controller/ReportControllerTest.java
+++ b/src/test/java/org/wooriverygood/api/report/api/ReportApiTest.java
@@ -1,28 +1,20 @@
-package org.wooriverygood.api.report.controller;
+package org.wooriverygood.api.report.api;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.wooriverygood.api.report.dto.ReportRequest;
-import org.wooriverygood.api.support.AuthInfo;
-import org.wooriverygood.api.util.ControllerTest;
+import org.wooriverygood.api.util.ApiTest;
-import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.Mockito.doNothing;
import static org.springframework.restdocs.mockmvc.MockMvcRestDocumentation.document;
-public class ReportControllerTest extends ControllerTest {
+public class ReportApiTest extends ApiTest {
@Test
@DisplayName("특정 게시글을 신고하면 201을 반환한다.")
void reportPost() {
- ReportRequest request = ReportRequest.builder()
- .message("신고 내용")
- .build();
-
- doNothing().when(reportService)
- .reportPost(any(Long.class), any(ReportRequest.class), any(AuthInfo.class));
+ ReportRequest request = new ReportRequest("신고 내용");
restDocs
.contentType(MediaType.APPLICATION_JSON_VALUE)
@@ -38,12 +30,7 @@ void reportPost() {
@Test
@DisplayName("특정 댓글을 신고하면 201을 반환한다.")
void reportComment() {
- ReportRequest request = ReportRequest.builder()
- .message("신고 내용")
- .build();
-
- doNothing().when(reportService)
- .reportComment(any(Long.class), any(ReportRequest.class), any(AuthInfo.class));
+ ReportRequest request = new ReportRequest("신고 내용");
restDocs
.contentType(MediaType.APPLICATION_JSON_VALUE)
@@ -55,5 +42,5 @@ void reportComment() {
.apply(document("comments/report/success"))
.statusCode(HttpStatus.CREATED.value());
}
-}
+}
diff --git a/src/test/java/org/wooriverygood/api/report/application/CommentReportServiceTest.java b/src/test/java/org/wooriverygood/api/report/application/CommentReportServiceTest.java
new file mode 100644
index 0000000..55961e3
--- /dev/null
+++ b/src/test/java/org/wooriverygood/api/report/application/CommentReportServiceTest.java
@@ -0,0 +1,95 @@
+package org.wooriverygood.api.report.application;
+
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.DisplayName;
+import org.junit.jupiter.api.Test;
+import org.mockito.InjectMocks;
+import org.mockito.Mock;
+import org.wooriverygood.api.comment.application.CommentServiceTest;
+import org.wooriverygood.api.comment.exception.CommentNotFoundException;
+import org.wooriverygood.api.member.repository.MemberRepository;
+import org.wooriverygood.api.post.domain.Post;
+import org.wooriverygood.api.post.domain.PostCategory;
+import org.wooriverygood.api.report.domain.CommentReport;
+import org.wooriverygood.api.report.exception.DuplicatedCommentReportException;
+import org.wooriverygood.api.comment.domain.Comment;
+import org.wooriverygood.api.comment.repository.CommentRepository;
+import org.wooriverygood.api.report.dto.ReportRequest;
+import org.wooriverygood.api.report.repository.CommentReportRepository;
+import org.wooriverygood.api.global.auth.AuthInfo;
+import org.wooriverygood.api.util.MockTest;
+
+import java.util.ArrayList;
+import java.util.Optional;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.assertj.core.api.Assertions.assertThatThrownBy;
+import static org.junit.jupiter.api.Assertions.assertAll;
+import static org.mockito.ArgumentMatchers.*;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+class CommentReportServiceTest extends CommentServiceTest {
+
+ @InjectMocks
+ private CommentReportService commentReportService;
+
+ @Mock
+ private CommentRepository commentRepository;
+
+ @Mock
+ private CommentReportRepository commentReportRepository;
+
+ @Mock
+ private MemberRepository memberRepository;
+
+ private Post post;
+
+
+ @BeforeEach
+ void setUp() {
+ post = Post.builder()
+ .id(6L)
+ .category(PostCategory.OFFER)
+ .title("title6")
+ .content("content6")
+ .member(member)
+ .comments(new ArrayList<>())
+ .postLikes(new ArrayList<>())
+ .build();
+ }
+
+ @Test
+ @DisplayName("유효한 id를 통해 특정 댓글을 신고한다.")
+ void reportComment() {
+ ReportRequest request = new ReportRequest("report message");
+ when(memberRepository.findById(anyLong()))
+ .thenReturn(Optional.ofNullable(member));
+ when(commentRepository.findById(anyLong()))
+ .thenReturn(Optional.ofNullable(comment));
+
+ commentReportService.reportComment(comment.getId(), request, authInfo);
+
+ assertAll(
+ () -> assertThat(comment.getReports().size()).isEqualTo(1),
+ () -> verify(commentRepository).increaseReportCount(comment.getId()),
+ () -> verify(commentReportRepository).save(any(CommentReport.class))
+ );
+ }
+
+ @Test
+ @DisplayName("동일한 댓글을 한 번 이상 신고하면 예외를 발생한다.")
+ void reportComment_exception_duplicated() {
+ ReportRequest request = new ReportRequest("report message");
+ when(memberRepository.findById(anyLong()))
+ .thenReturn(Optional.ofNullable(member));
+ when(commentRepository.findById(anyLong()))
+ .thenReturn(Optional.ofNullable(comment));
+
+ commentReportService.reportComment(comment.getId(), request, authInfo);
+
+ assertThatThrownBy(() -> commentReportService.reportComment(1L, request, authInfo))
+ .isInstanceOf(DuplicatedCommentReportException.class);
+ }
+
+}
\ No newline at end of file
diff --git a/src/test/java/org/wooriverygood/api/report/application/PostReportServiceTest.java b/src/test/java/org/wooriverygood/api/report/application/PostReportServiceTest.java
new file mode 100644
index 0000000..f5fe642
--- /dev/null
+++ b/src/test/java/org/wooriverygood/api/report/application/PostReportServiceTest.java
@@ -0,0 +1,84 @@
+package org.wooriverygood.api.report.application;
+
+import org.junit.jupiter.api.DisplayName;
+import org.junit.jupiter.api.Test;
+import org.mockito.InjectMocks;
+import org.mockito.Mock;
+import org.wooriverygood.api.member.domain.Member;
+import org.wooriverygood.api.member.repository.MemberRepository;
+import org.wooriverygood.api.post.application.PostServiceTest;
+import org.wooriverygood.api.post.exception.PostNotFoundException;
+import org.wooriverygood.api.post.repository.PostRepository;
+import org.wooriverygood.api.report.domain.PostReport;
+import org.wooriverygood.api.report.dto.ReportRequest;
+import org.wooriverygood.api.report.exception.DuplicatedPostReportException;
+import org.wooriverygood.api.report.repository.PostReportRepository;
+
+import java.util.Optional;
+
+import static org.assertj.core.api.Assertions.assertThatThrownBy;
+import static org.junit.jupiter.api.Assertions.assertAll;
+import static org.mockito.ArgumentMatchers.*;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+class PostReportServiceTest extends PostServiceTest {
+
+ @InjectMocks
+ private PostReportService postReportService;
+
+ @Mock
+ private PostRepository postRepository;
+
+ @Mock
+ private PostReportRepository postReportRepository;
+
+ @Mock
+ private MemberRepository memberRepository;
+
+
+ @Test
+ @DisplayName("유효한 id를 통해 특정 게시글을 신고한다.")
+ void reportPost() {
+ ReportRequest request = new ReportRequest("report message");
+ when(memberRepository.findById(anyLong()))
+ .thenReturn(Optional.ofNullable(member));
+ when(postRepository.findById(anyLong()))
+ .thenReturn(Optional.ofNullable(post));
+
+ postReportService.reportPost(1L, request, authInfo);
+
+ assertAll(
+ () -> verify(postRepository).increaseReportCount(post.getId()),
+ () -> verify(postReportRepository).save(any(PostReport.class))
+ );
+ }
+
+ @Test
+ @DisplayName("신고하려는 게시글 id가 유효하지 않으면 예외를 발생한다.")
+ void reportPost_exception_invalidId() {
+ ReportRequest request = new ReportRequest("report message");
+ when(memberRepository.findById(anyLong()))
+ .thenReturn(Optional.ofNullable(member));
+ when(postRepository.findById(anyLong()))
+ .thenThrow(new PostNotFoundException());
+
+ assertThatThrownBy(() -> postReportService.reportPost(post.getId(), request, authInfo))
+ .isInstanceOf(PostNotFoundException.class);
+ }
+
+ @Test
+ @DisplayName("동일한 게시글을 한 번 이상 신고하면 예외를 발생한다.")
+ void reportPost_exception_duplicated() {
+ ReportRequest request = new ReportRequest("report message");
+ when(memberRepository.findById(anyLong()))
+ .thenReturn(Optional.ofNullable(member));
+ when(postRepository.findById(anyLong()))
+ .thenReturn(Optional.ofNullable(post));
+ postReportService.reportPost(post.getId(), request, authInfo);
+
+ assertThatThrownBy(() -> postReportService.reportPost(post.getId(), request, authInfo))
+ .isInstanceOf(DuplicatedPostReportException.class);
+ }
+
+}
\ No newline at end of file
diff --git a/src/test/java/org/wooriverygood/api/report/service/ReportServiceTest.java b/src/test/java/org/wooriverygood/api/report/service/ReportServiceTest.java
deleted file mode 100644
index cf41495..0000000
--- a/src/test/java/org/wooriverygood/api/report/service/ReportServiceTest.java
+++ /dev/null
@@ -1,172 +0,0 @@
-package org.wooriverygood.api.report.service;
-
-import org.junit.jupiter.api.BeforeEach;
-import org.junit.jupiter.api.DisplayName;
-import org.junit.jupiter.api.Test;
-import org.junit.jupiter.api.extension.ExtendWith;
-import org.mockito.InjectMocks;
-import org.mockito.Mock;
-import org.mockito.junit.jupiter.MockitoExtension;
-import org.wooriverygood.api.advice.exception.CommentNotFoundException;
-import org.wooriverygood.api.advice.exception.DuplicatedCommentReportException;
-import org.wooriverygood.api.advice.exception.DuplicatedPostReportException;
-import org.wooriverygood.api.advice.exception.PostNotFoundException;
-import org.wooriverygood.api.comment.domain.Comment;
-import org.wooriverygood.api.comment.repository.CommentRepository;
-import org.wooriverygood.api.post.domain.Post;
-import org.wooriverygood.api.post.domain.PostCategory;
-import org.wooriverygood.api.post.repository.PostRepository;
-import org.wooriverygood.api.report.dto.ReportRequest;
-import org.wooriverygood.api.report.repository.CommentReportRepository;
-import org.wooriverygood.api.report.repository.PostReportRepository;
-import org.wooriverygood.api.support.AuthInfo;
-
-import java.util.ArrayList;
-import java.util.Optional;
-
-import static org.assertj.core.api.Assertions.assertThat;
-import static org.assertj.core.api.Assertions.assertThatThrownBy;
-import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.Mockito.when;
-
-@ExtendWith(MockitoExtension.class)
-class ReportServiceTest {
-
- @InjectMocks
- private ReportService reportService;
-
- @Mock
- private CommentRepository commentRepository;
-
- @Mock
- private CommentReportRepository commentReportRepository;
-
- @Mock
- private PostRepository postRepository;
-
- @Mock
- private PostReportRepository postReportRepository;
-
- private AuthInfo testAuthInfo;
-
- private Post testPost;
-
- private Comment testComment;
-
-
- @BeforeEach
- void setUp() {
- testAuthInfo = AuthInfo.builder()
- .sub("22432-12312-3531")
- .username("22432-12312-3531")
- .build();
-
- testPost = Post.builder()
- .id(1L)
- .category(PostCategory.OFFER)
- .title("post title")
- .content("post content")
- .author(testAuthInfo.getUsername())
- .comments(new ArrayList<>())
- .postLikes(new ArrayList<>())
- .reports(new ArrayList<>())
- .build();
-
- testComment = Comment.builder()
- .id(1L)
- .content("comment content")
- .author(testAuthInfo.getUsername())
- .commentLikes(new ArrayList<>())
- .reports(new ArrayList<>())
- .build();
- }
-
- @Test
- @DisplayName("유효한 id를 통해 특정 게시글을 신고한다.")
- void reportPost() {
- ReportRequest request = ReportRequest.builder()
- .message("report message")
- .build();
-
- when(postRepository.findById(any(Long.class)))
- .thenReturn(Optional.ofNullable(testPost));
-
- reportService.reportPost(1L, request, testAuthInfo);
-
- assertThat(testPost.getReports().size()).isEqualTo(1);
- }
-
- @Test
- @DisplayName("신고하려는 게시글 id가 유효하지 않으면 예외를 발생한다.")
- void reportPost_exception_invalidId() {
- ReportRequest request = ReportRequest.builder()
- .message("report message")
- .build();
-
- when(postRepository.findById(any(Long.class)))
- .thenThrow(new PostNotFoundException());
-
- assertThatThrownBy(() -> reportService.reportPost(1L, request, testAuthInfo))
- .isInstanceOf(PostNotFoundException.class);
- }
-
- @Test
- @DisplayName("동일한 게시글을 한 번 이상 신고하면 예외를 발생한다.")
- void reportPost_exception_duplicated() {
- ReportRequest request = ReportRequest.builder()
- .message("report message")
- .build();
-
- when(postRepository.findById(any(Long.class)))
- .thenReturn(Optional.ofNullable(testPost));
- reportService.reportPost(1L, request, testAuthInfo);
-
- assertThatThrownBy(() -> reportService.reportPost(1L, request, testAuthInfo))
- .isInstanceOf(DuplicatedPostReportException.class);
- }
-
- @Test
- @DisplayName("유효한 id를 통해 특정 댓글을 신고한다.")
- void reportComment() {
- ReportRequest request = ReportRequest.builder()
- .message("report message")
- .build();
-
- when(commentRepository.findById(any(Long.class)))
- .thenReturn(Optional.ofNullable(testComment));
-
- reportService.reportComment(1L, request, testAuthInfo);
-
- assertThat(testComment.getReports().size()).isEqualTo(1);
- }
-
- @Test
- @DisplayName("신고하려는 댓글의 id가 유효하지 않으면 예외를 발생한다.")
- void reportComment_exception_invalidId() {
- ReportRequest request = ReportRequest.builder()
- .message("report message")
- .build();
-
- when(commentRepository.findById(any(Long.class)))
- .thenThrow(new CommentNotFoundException());
-
- assertThatThrownBy(() -> reportService.reportComment(1L, request, testAuthInfo))
- .isInstanceOf(CommentNotFoundException.class);
- }
-
- @Test
- @DisplayName("동일한 댓글을 한 번 이상 신고하면 예외를 발생한다.")
- void reportComment_exception_duplicated() {
- ReportRequest request = ReportRequest.builder()
- .message("report message")
- .build();
-
- when(commentRepository.findById(any(Long.class)))
- .thenReturn(Optional.ofNullable(testComment));
- reportService.reportComment(1L, request, testAuthInfo);
-
- assertThatThrownBy(() -> reportService.reportComment(1L, request, testAuthInfo))
- .isInstanceOf(DuplicatedCommentReportException.class);
- }
-
-}
\ No newline at end of file
diff --git a/src/test/java/org/wooriverygood/api/review/controller/ReviewControllerTest.java b/src/test/java/org/wooriverygood/api/review/api/ReviewApiTest.java
similarity index 54%
rename from src/test/java/org/wooriverygood/api/review/controller/ReviewControllerTest.java
rename to src/test/java/org/wooriverygood/api/review/api/ReviewApiTest.java
index d6fc435..45ad5bf 100644
--- a/src/test/java/org/wooriverygood/api/review/controller/ReviewControllerTest.java
+++ b/src/test/java/org/wooriverygood/api/review/api/ReviewApiTest.java
@@ -1,55 +1,57 @@
-package org.wooriverygood.api.review.controller;
+package org.wooriverygood.api.review.api;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
-import org.mockito.Mockito;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
-import org.wooriverygood.api.advice.exception.AuthorizationException;
-import org.wooriverygood.api.advice.exception.CourseNotFoundException;
-import org.wooriverygood.api.advice.exception.ReviewAccessDeniedException;
-import org.wooriverygood.api.course.domain.Courses;
+import org.wooriverygood.api.global.error.exception.AuthorizationException;
+import org.wooriverygood.api.review.exception.ReviewAccessDeniedException;
+import org.wooriverygood.api.course.domain.Course;
import org.wooriverygood.api.review.dto.*;
-import org.wooriverygood.api.support.AuthInfo;
-import org.wooriverygood.api.util.ControllerTest;
+import org.wooriverygood.api.global.auth.AuthInfo;
+import org.wooriverygood.api.util.ApiTest;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.List;
import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyLong;
+import static org.mockito.Mockito.*;
import static org.springframework.restdocs.mockmvc.MockMvcRestDocumentation.document;
-public class ReviewControllerTest extends ControllerTest {
- List responses = new ArrayList<>();
+public class ReviewApiTest extends ApiTest {
- Courses course= Courses.builder()
+ private List responses = new ArrayList<>();
+
+ private Course course = Course.builder()
.id(1L)
- .course_name("Gaoshu")
- .course_category("Zhuanye")
- .course_credit(5)
+ .name("Gaoshu")
+ .category("Zhuanye")
+ .credit(5)
.isYouguan(0)
.kaikeYuanxi("Xinke")
.build();
- AuthInfo authInfo = AuthInfo.builder()
+ private AuthInfo authInfo = AuthInfo.builder()
.sub("22222-34534-123")
.username("22222-34534-123")
.build();
+
@BeforeEach
void setUp() {
for(int i = 1; i <= 2; i++) {
responses.add(ReviewResponse.builder()
- .review_id((long)i)
- .course_id(1L)
- .review_content("Test Review " + i)
- .review_title("Title" + i)
- .instructor_name("Jiaoshou")
- .taken_semyr("22-23")
+ .reviewId((long)i)
+ .courseId(1L)
+ .reviewContent("Test Review " + i)
+ .reviewTitle("Title" + i)
+ .instructorName("Jiaoshou")
+ .takenSemyr("22-23")
.grade("60")
- .review_time(LocalDateTime.now())
+ .reviewTime(LocalDateTime.now())
.isMine(false)
.updated(false)
.build());
@@ -59,10 +61,11 @@ void setUp() {
@Test
@DisplayName("최근 작성한 리뷰가 6개월 미만이라면, 특정 강의의 리뷰 조회 요청을 받으면 리뷰들을 반환한다.")
void findAllReviewsByCourseId_success() {
- Mockito.when(reviewService.canAccessReviews(any(AuthInfo.class)))
- .thenReturn(true);
- Mockito.when(reviewService.findAllReviewsByCourseId(any(), any(AuthInfo.class)))
- .thenReturn(responses);
+ doNothing()
+ .when(reviewValidateAccessService)
+ .validateReviewAccess(any(AuthInfo.class));
+ when(reviewFindService.findAllReviewsByCourseId(anyLong(), any(AuthInfo.class)))
+ .thenReturn(new ReviewsResponse(responses));
restDocs
.contentType(MediaType.APPLICATION_JSON_VALUE)
@@ -76,8 +79,9 @@ void findAllReviewsByCourseId_success() {
@Test
@DisplayName("최근 작성한 리뷰가 6개월 이상이거나 없다면, 리뷰 요청에 대한 BadRequest 예외를 반환한다.")
void findAllReviewsByCourseId_exception_accessDenied() {
- Mockito.when(reviewService.canAccessReviews(any(AuthInfo.class)))
- .thenThrow(new ReviewAccessDeniedException());
+ doThrow(new ReviewAccessDeniedException())
+ .when(reviewValidateAccessService)
+ .validateReviewAccess(any(AuthInfo.class));
restDocs
.contentType(MediaType.APPLICATION_JSON_VALUE)
@@ -88,48 +92,17 @@ void findAllReviewsByCourseId_exception_accessDenied() {
.statusCode(HttpStatus.BAD_REQUEST.value());
}
- @Test
- @DisplayName("유효하지 않은 수업의 리뷰를 조회하면 404를 반환한다.")
- void findReviews_exception_invalidId() {
- Mockito.when(reviewService.canAccessReviews(any(AuthInfo.class)))
- .thenReturn(true);
- Mockito.when(reviewService.findAllReviewsByCourseId(any(Long.class), any(AuthInfo.class)))
- .thenThrow(new CourseNotFoundException());
-
- restDocs
- .contentType(MediaType.APPLICATION_JSON_VALUE)
- .header("Authorization", "Bearer aws-cognito-access-token")
- .when().get("/courses/1/reviews")
- .then().log().all()
- .assertThat()
- .apply(document("reviews/find/fail/noCourse"))
- .statusCode(HttpStatus.NOT_FOUND.value());
- }
-
@Test
@DisplayName("특정 강의의 리뷰를 작성한다.")
void addReview() {
NewReviewRequest request = NewReviewRequest.builder()
- .review_title("Test Review from TestCode")
- .instructor_name("Jiaoshou")
- .taken_semyr("1stSem")
- .review_content("Good!")
+ .reviewTitle("Test Review from TestCode")
+ .instructorName("Jiaoshou")
+ .takenSemyr("1stSem")
+ .reviewContent("Good!")
.grade("100")
.build();
- NewReviewResponse response = NewReviewResponse.builder()
- .review_id(50L)
- .author_email(authInfo.getUsername())
- .review_title("Test Review from TestCode")
- .instructor_name("Jiaoshou")
- .taken_semyr("1stSem")
- .review_content("Good!")
- .grade("100")
- .build();
-
- Mockito.when(reviewService.addReview(any(AuthInfo.class), any(Long.class), any(NewReviewRequest.class)))
- .thenReturn(response);
-
restDocs
.contentType(MediaType.APPLICATION_JSON_VALUE)
.header("Authorization", "Bearer aws-cognito-access-token")
@@ -144,16 +117,16 @@ void addReview() {
@Test
@DisplayName("특정 리뷰의 좋아요를 1 올리거나 내린다.")
void likeReview() {
- Mockito.when(reviewService.likeReview(any(Long.class), any(AuthInfo.class)))
+ when(reviewLikeToggleService.toggleReviewLike(any(Long.class), any(AuthInfo.class)))
.thenReturn(ReviewLikeResponse.builder()
- .like_count(5)
+ .likeCount(5)
.liked(false)
.build());
restDocs
.contentType(MediaType.APPLICATION_JSON_VALUE)
.header("Authorization", "Bearer aws-cognito-access-token")
- .when().put("/courses/reviews/1/like")
+ .when().put("/reviews/1/like")
.then().log().all()
.assertThat()
.apply(document("reviews/like/success"))
@@ -166,26 +139,26 @@ void findMyReviews() {
List responses = new ArrayList<>();
for (int i = 1; i < 5; i++) {
responses.add(ReviewResponse.builder()
- .review_id((long)i)
- .course_id(1L)
- .review_content("Test Review " + i)
- .review_title("Title" + i)
- .instructor_name("Jiaoshou")
- .taken_semyr("22-23")
+ .reviewId((long)i)
+ .courseId(1L)
+ .reviewContent("Test Review " + i)
+ .reviewTitle("Title" + i)
+ .instructorName("Jiaoshou")
+ .takenSemyr("22-23")
.grade("60")
- .review_time(LocalDateTime.now())
+ .reviewTime(LocalDateTime.now())
.isMine(true)
.updated(false)
.build());
}
- Mockito.when(reviewService.findMyReviews(any(AuthInfo.class)))
- .thenReturn(responses);
+ when(reviewFindService.findMyReviews(any(AuthInfo.class)))
+ .thenReturn(new ReviewsResponse(responses));
restDocs
.contentType(MediaType.APPLICATION_JSON_VALUE)
.header("Authorization", "Bearer aws-cognito-access-token")
- .when().get("/courses/reviews/me")
+ .when().get("/reviews/me")
.then().log().all()
.assertThat()
.apply(document("reviews/find/me/success"))
@@ -196,44 +169,41 @@ void findMyReviews() {
@DisplayName("권한이 있는 리뷰를 수정한다.")
void updateReview() {
ReviewUpdateRequest request = ReviewUpdateRequest.builder()
- .review_title("new title")
- .review_content("new content")
- .instructor_name("jiaoshou")
- .taken_semyr("18-19")
+ .reviewTitle("new title")
+ .reviewContent("new content")
+ .instructorName("jiaoshou")
+ .takenSemyr("18-19")
.grade("100")
.build();
- Mockito.when(reviewService.updateReview(any(Long.class), any(ReviewUpdateRequest.class), any(AuthInfo.class)))
- .thenReturn(ReviewUpdateResponse.builder()
- .review_id((long)1)
- .build());
restDocs
.contentType(MediaType.APPLICATION_JSON_VALUE)
.header("Authorization", "Bearer aws-cognito-access-token")
.body(request)
- .when().put("/courses/reviews/1")
+ .when().put("/reviews/1")
.then().log().all()
.assertThat()
.apply(document("reviews/update/success"))
- .statusCode(HttpStatus.OK.value());
+ .statusCode(HttpStatus.NO_CONTENT.value());
}
@Test
@DisplayName("권한이 없는 리뷰를 수정하면 403을 반환한다.")
void updateReview_noAuth() {
ReviewUpdateRequest request = ReviewUpdateRequest.builder()
- .review_title("new title")
- .review_content("new content")
+ .reviewTitle("new title")
+ .reviewContent("new content")
.build();
- Mockito.when(reviewService.updateReview(any(Long.class), any(ReviewUpdateRequest.class), any(AuthInfo.class)))
- .thenThrow(new AuthorizationException());
+ doThrow(new AuthorizationException())
+ .when(reviewUpdateService)
+ .updateReview(anyLong(), any(ReviewUpdateRequest.class), any(AuthInfo.class));
restDocs
.contentType(MediaType.APPLICATION_JSON_VALUE)
.header("Authorization", "Bearer aws-cognito-access-token")
.body(request)
- .when().put("/courses/reviews/1")
+ .when().put("/reviews/1")
.then().log().all()
.assertThat()
.apply(document("reviews/update/fail/noAuth"))
@@ -243,35 +213,29 @@ void updateReview_noAuth() {
@Test
@DisplayName("권한이 있는 리뷰를 삭제한다.")
void deleteReview() {
- Mockito.when(reviewService.deleteReview(any(Long.class), any(AuthInfo.class)))
- .thenReturn(ReviewDeleteResponse.builder()
- .review_id(8L)
- .build());
-
restDocs
.header("Authorization", "any")
- .when().delete("/courses/reviews/8")
+ .when().delete("/reviews/8")
.then().log().all()
.assertThat()
.apply(document("reviews/delete/success"))
- .statusCode(HttpStatus.OK.value());
+ .statusCode(HttpStatus.NO_CONTENT.value());
}
@Test
@DisplayName("권한이 없는 리뷰를 삭제하면 403을 반환한다.")
void deleteReview_noAuth() {
- Mockito.when(reviewService.deleteReview(any(Long.class), any(AuthInfo.class)))
- .thenThrow(new AuthorizationException());
+ doThrow(new AuthorizationException())
+ .when(reviewDeleteService)
+ .deleteReview(anyLong(), any(AuthInfo.class));
restDocs
.header("Authorization", "any")
- .when().delete("/courses/reviews/8")
+ .when().delete("/reviews/8")
.then().log().all()
.assertThat()
.apply(document("reviews/delete/fail/noAuth"))
.statusCode(HttpStatus.FORBIDDEN.value());
}
-
-
}
diff --git a/src/test/java/org/wooriverygood/api/review/application/ReviewCreateServiceTest.java b/src/test/java/org/wooriverygood/api/review/application/ReviewCreateServiceTest.java
new file mode 100644
index 0000000..a5ac22d
--- /dev/null
+++ b/src/test/java/org/wooriverygood/api/review/application/ReviewCreateServiceTest.java
@@ -0,0 +1,71 @@
+package org.wooriverygood.api.review.application;
+
+import org.junit.jupiter.api.DisplayName;
+import org.junit.jupiter.api.Test;
+import org.mockito.InjectMocks;
+import org.mockito.Mock;
+import org.wooriverygood.api.course.domain.Course;
+import org.wooriverygood.api.course.repository.CourseRepository;
+import org.wooriverygood.api.global.auth.AuthInfo;
+import org.wooriverygood.api.member.repository.MemberRepository;
+import org.wooriverygood.api.review.domain.Review;
+import org.wooriverygood.api.review.dto.NewReviewRequest;
+import org.wooriverygood.api.review.repository.ReviewRepository;
+import org.wooriverygood.api.util.MockTest;
+
+import java.util.Optional;
+
+import static org.junit.jupiter.api.Assertions.assertAll;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyLong;
+import static org.mockito.Mockito.*;
+
+class ReviewCreateServiceTest extends MockTest {
+
+ @InjectMocks
+ private ReviewCreateService reviewCreateService;
+
+ @Mock
+ private CourseRepository courseRepository;
+
+ @Mock
+ private ReviewRepository reviewRepository;
+
+ @Mock
+ private MemberRepository memberRepository;
+
+ private Course course = Course.builder()
+ .id(1L)
+ .name("Gaoshu")
+ .category("Zhuanye")
+ .credit(5)
+ .isYouguan(0)
+ .kaikeYuanxi("Xinke")
+ .build();
+
+
+ @Test
+ @DisplayName("특정 강의의 리뷰를 작성한다.")
+ void addReview() {
+ NewReviewRequest request = NewReviewRequest.builder()
+ .reviewTitle("Test Review from TestCode")
+ .instructorName("Jiaoshou")
+ .takenSemyr("1stSem")
+ .reviewContent("Good!")
+ .grade("100")
+ .build();
+
+ when(courseRepository.findById(anyLong()))
+ .thenReturn(Optional.ofNullable(course));
+ when(memberRepository.findById(anyLong()))
+ .thenReturn(Optional.ofNullable(member));
+
+ reviewCreateService.addReview(authInfo, 1L, request);
+
+ assertAll(
+ () -> verify(reviewRepository).save(any(Review.class)),
+ () -> verify(courseRepository).increaseReviewCount(course.getId())
+ );
+ }
+
+}
\ No newline at end of file
diff --git a/src/test/java/org/wooriverygood/api/review/application/ReviewDeleteServiceTest.java b/src/test/java/org/wooriverygood/api/review/application/ReviewDeleteServiceTest.java
new file mode 100644
index 0000000..0099a3b
--- /dev/null
+++ b/src/test/java/org/wooriverygood/api/review/application/ReviewDeleteServiceTest.java
@@ -0,0 +1,101 @@
+package org.wooriverygood.api.review.application;
+
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.DisplayName;
+import org.junit.jupiter.api.Test;
+import org.mockito.InjectMocks;
+import org.mockito.Mock;
+import org.wooriverygood.api.course.domain.Course;
+import org.wooriverygood.api.course.repository.CourseRepository;
+import org.wooriverygood.api.global.error.exception.AuthorizationException;
+import org.wooriverygood.api.member.domain.Member;
+import org.wooriverygood.api.member.repository.MemberRepository;
+import org.wooriverygood.api.review.domain.Review;
+import org.wooriverygood.api.review.exception.ReviewNotFoundException;
+import org.wooriverygood.api.review.repository.ReviewLikeRepository;
+import org.wooriverygood.api.review.repository.ReviewRepository;
+import org.wooriverygood.api.util.MockTest;
+
+import java.util.Optional;
+
+import static org.assertj.core.api.Assertions.assertThatThrownBy;
+import static org.junit.jupiter.api.Assertions.assertAll;
+import static org.mockito.ArgumentMatchers.anyLong;
+import static org.mockito.Mockito.*;
+
+class ReviewDeleteServiceTest extends MockTest {
+
+ @InjectMocks
+ private ReviewDeleteService reviewDeleteService;
+
+ @Mock
+ private CourseRepository courseRepository;
+
+ @Mock
+ private ReviewRepository reviewRepository;
+
+ @Mock
+ private ReviewLikeRepository reviewLikeRepository;
+
+ @Mock
+ private MemberRepository memberRepository;
+
+ private Course course;
+
+ private Review review;
+
+ @BeforeEach
+ void setUp() {
+ course = Course.builder()
+ .id(1L)
+ .build();
+ review = Review.builder()
+ .member(member)
+ .course(course)
+ .build();
+ }
+
+ @Test
+ @DisplayName("권한이 있는 리뷰를 삭제한다.")
+ void deleteReview() {
+ when(reviewRepository.findById(anyLong()))
+ .thenReturn(Optional.ofNullable(review));
+ when(memberRepository.findById(anyLong()))
+ .thenReturn(Optional.ofNullable(member));
+
+ reviewDeleteService.deleteReview(1L, authInfo);
+
+ assertAll(
+ () -> verify(reviewLikeRepository).deleteAllByReview(review),
+ () -> verify(reviewRepository).delete(review),
+ () -> verify(courseRepository).decreaseReviewCount(course.getId())
+ );
+ }
+
+ @Test
+ @DisplayName("권한이 없는 리뷰는 삭제가 불가능하다.")
+ void deleteReview_exception_noAuth() {
+ Review review = Review.builder()
+ .id(2L)
+ .member(new Member(5L, "noAuth"))
+ .build();
+ when(reviewRepository.findById(anyLong()))
+ .thenReturn(Optional.ofNullable(review));
+ when(memberRepository.findById(anyLong()))
+ .thenReturn(Optional.ofNullable(member));
+
+ assertThatThrownBy(() -> reviewDeleteService.deleteReview(1L, authInfo))
+ .isInstanceOf(AuthorizationException.class);
+ }
+
+ @Test
+ @DisplayName("리뷰를 찾을 수 없으면, 예외를 발생시킨다.")
+ void deleteReview_exception_reviewNotFound() {
+ when(reviewRepository.findById(anyLong()))
+ .thenThrow(new ReviewNotFoundException());
+
+ assertThatThrownBy(() -> reviewDeleteService.deleteReview(1L, authInfo))
+ .isInstanceOf(ReviewNotFoundException.class);
+ }
+
+}
\ No newline at end of file
diff --git a/src/test/java/org/wooriverygood/api/review/application/ReviewFindServiceTest.java b/src/test/java/org/wooriverygood/api/review/application/ReviewFindServiceTest.java
new file mode 100644
index 0000000..b3fa693
--- /dev/null
+++ b/src/test/java/org/wooriverygood/api/review/application/ReviewFindServiceTest.java
@@ -0,0 +1,102 @@
+package org.wooriverygood.api.review.application;
+
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.DisplayName;
+import org.junit.jupiter.api.Test;
+import org.mockito.InjectMocks;
+import org.mockito.Mock;
+import org.wooriverygood.api.course.domain.Course;
+import org.wooriverygood.api.member.domain.Member;
+import org.wooriverygood.api.member.repository.MemberRepository;
+import org.wooriverygood.api.review.domain.Review;
+import org.wooriverygood.api.review.dto.ReviewsResponse;
+import org.wooriverygood.api.review.repository.ReviewLikeRepository;
+import org.wooriverygood.api.review.repository.ReviewRepository;
+import org.wooriverygood.api.util.MockTest;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Optional;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.mockito.ArgumentMatchers.*;
+import static org.mockito.Mockito.when;
+
+class ReviewFindServiceTest extends MockTest {
+
+ @InjectMocks
+ private ReviewFindService reviewFindService;
+
+ @Mock
+ private ReviewRepository reviewRepository;
+
+ @Mock
+ private ReviewLikeRepository reviewLikeRepository;
+
+ @Mock
+ private MemberRepository memberRepository;
+
+ private List reviews = new ArrayList<>();
+
+ private Course course = Course.builder()
+ .id(1L)
+ .name("Gaoshu")
+ .category("Zhuanye")
+ .credit(5)
+ .isYouguan(0)
+ .kaikeYuanxi("Xinke")
+ .build();
+
+ private final int REVIEW_COUNT = 10;
+
+
+ @BeforeEach
+ void setUp() {
+ for(long i = 0; i < REVIEW_COUNT; i++) {
+ Review review = Review.builder()
+ .id(i)
+ .course(course)
+ .member(member)
+ .reviewContent("review" + i)
+ .reviewTitle("review" + i)
+ .instructorName("jiaoshou")
+ .takenSemyr("22-23")
+ .grade("60")
+ .reviewLikes(new ArrayList<>())
+ .updated(false)
+ .build();
+ reviews.add(review);
+ }
+ }
+
+ @Test
+ @DisplayName("유효한 id를 통해 특정 수업의 리뷰들을 불러온다.")
+ void findAllReviewsByCourseId() {
+ when(reviewRepository.findAllByCourseId(any()))
+ .thenReturn(reviews);
+ when(memberRepository.findById(anyLong()))
+ .thenReturn(Optional.ofNullable(member));
+ when(reviewLikeRepository.existsByReviewAndMember(any(Review.class), any(Member.class)))
+ .thenReturn(true);
+
+ ReviewsResponse response = reviewFindService.findAllReviewsByCourseId(1L, authInfo);
+
+ assertThat(response.getReviews()).hasSize(REVIEW_COUNT);
+ assertThat(response.getReviews().get(0).getReviewTitle()).isEqualTo("review0");
+ }
+
+ @Test
+ @DisplayName("사용자 본인이 작성한 리뷰들을 불러온다.")
+ void findMyReviews() {
+ for (Review review : reviews) {
+ member.addReview(review);
+ }
+ when(memberRepository.findById(anyLong()))
+ .thenReturn(Optional.ofNullable(member));
+
+ ReviewsResponse response = reviewFindService.findMyReviews(authInfo);
+
+ assertThat(response.getReviews().get(0).isMine()).isEqualTo(true);
+ }
+
+}
\ No newline at end of file
diff --git a/src/test/java/org/wooriverygood/api/review/application/ReviewLikeToggleServiceTest.java b/src/test/java/org/wooriverygood/api/review/application/ReviewLikeToggleServiceTest.java
new file mode 100644
index 0000000..c9f114e
--- /dev/null
+++ b/src/test/java/org/wooriverygood/api/review/application/ReviewLikeToggleServiceTest.java
@@ -0,0 +1,91 @@
+package org.wooriverygood.api.review.application;
+
+import org.junit.jupiter.api.DisplayName;
+import org.junit.jupiter.api.Test;
+import org.mockito.InjectMocks;
+import org.mockito.Mock;
+import org.wooriverygood.api.member.domain.Member;
+import org.wooriverygood.api.member.repository.MemberRepository;
+import org.wooriverygood.api.review.domain.Review;
+import org.wooriverygood.api.review.domain.ReviewLike;
+import org.wooriverygood.api.review.dto.*;
+import org.wooriverygood.api.review.repository.ReviewLikeRepository;
+import org.wooriverygood.api.review.repository.ReviewRepository;
+import org.wooriverygood.api.util.MockTest;
+
+import java.time.LocalDateTime;
+import java.util.ArrayList;
+import java.util.Optional;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.junit.jupiter.api.Assertions.assertAll;
+import static org.mockito.ArgumentMatchers.*;
+import static org.mockito.Mockito.when;
+
+public class ReviewLikeToggleServiceTest extends MockTest {
+
+ @InjectMocks
+ private ReviewLikeToggleService reviewLikeToggleService;
+
+ @Mock
+ private ReviewRepository reviewRepository;
+
+ @Mock
+ private ReviewLikeRepository reviewLikeRepository;
+
+ @Mock
+ private MemberRepository memberRepository;
+
+ private final Review review = Review.builder()
+ .id(1L)
+ .reviewContent("test review")
+ .reviewTitle("test review title")
+ .instructorName("jiaoshou")
+ .takenSemyr("22-23")
+ .grade("100")
+ .member(member)
+ .reviewLikes(new ArrayList<>())
+ .updated(false)
+ .createdAt(LocalDateTime.of(2022, 6, 13, 12, 00))
+ .build();
+
+
+ @Test
+ @DisplayName("특정 리뷰의 좋아요를 1 올린다.")
+ void likeReview_up() {
+ when(reviewRepository.findById(anyLong()))
+ .thenReturn(Optional.ofNullable(review));
+ when(memberRepository.findById(anyLong()))
+ .thenReturn(Optional.ofNullable(member));
+
+ ReviewLikeResponse response = reviewLikeToggleService.toggleReviewLike(review.getId(), authInfo);
+
+ assertAll(
+ () -> assertThat(response.getLikeCount()).isEqualTo(review.getLikeCount() + 1),
+ () -> assertThat(response.isLiked()).isEqualTo(true)
+ );
+ }
+
+ @Test
+ @DisplayName("특정 리뷰의 좋아요를 1 내린다.")
+ void likeReview_down() {
+ ReviewLike reviewLike = ReviewLike.builder()
+ .id(3L)
+ .review(review)
+ .member(member)
+ .build();
+
+ when(reviewRepository.findById(anyLong()))
+ .thenReturn(Optional.ofNullable(review));
+ when(memberRepository.findById(anyLong()))
+ .thenReturn(Optional.ofNullable(member));
+ when(reviewLikeRepository.findByReviewAndMember(any(Review.class), any(Member.class)))
+ .thenReturn(Optional.ofNullable(reviewLike));
+
+ ReviewLikeResponse response = reviewLikeToggleService.toggleReviewLike(review.getId(), authInfo);
+
+ assertThat(response.getLikeCount()).isEqualTo(review.getLikeCount() - 1);
+ assertThat(response.isLiked()).isEqualTo(false);
+ }
+
+}
diff --git a/src/test/java/org/wooriverygood/api/review/application/ReviewUpdateServiceTest.java b/src/test/java/org/wooriverygood/api/review/application/ReviewUpdateServiceTest.java
new file mode 100644
index 0000000..5944d58
--- /dev/null
+++ b/src/test/java/org/wooriverygood/api/review/application/ReviewUpdateServiceTest.java
@@ -0,0 +1,85 @@
+package org.wooriverygood.api.review.application;
+
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.DisplayName;
+import org.junit.jupiter.api.Test;
+import org.mockito.InjectMocks;
+import org.mockito.Mock;
+import org.wooriverygood.api.global.error.exception.AuthorizationException;
+import org.wooriverygood.api.member.domain.Member;
+import org.wooriverygood.api.member.repository.MemberRepository;
+import org.wooriverygood.api.review.domain.Review;
+import org.wooriverygood.api.review.dto.ReviewUpdateRequest;
+import org.wooriverygood.api.review.repository.ReviewRepository;
+import org.wooriverygood.api.util.MockTest;
+
+import java.util.Optional;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.assertj.core.api.Assertions.assertThatThrownBy;
+import static org.junit.jupiter.api.Assertions.assertAll;
+import static org.mockito.Mockito.*;
+
+class ReviewUpdateServiceTest extends MockTest {
+
+ @InjectMocks
+ private ReviewUpdateService reviewUpdateService;
+
+ @Mock
+ private ReviewRepository reviewRepository;
+
+ @Mock
+ private MemberRepository memberRepository;
+
+ private Review review;
+
+ @BeforeEach
+ void setUp() {
+ review = Review.builder()
+ .id(1L)
+ .member(member)
+ .build();
+ }
+
+ @Test
+ @DisplayName("권한이 있는 리뷰를 수정한다.")
+ void updateReview() {
+ ReviewUpdateRequest request = ReviewUpdateRequest.builder()
+ .reviewTitle("new title")
+ .reviewContent("new content")
+ .instructorName("jiaoshou")
+ .takenSemyr("18-19")
+ .grade("100")
+ .build();
+ when(reviewRepository.findById(anyLong()))
+ .thenReturn(Optional.ofNullable(review));
+ when(memberRepository.findById(anyLong()))
+ .thenReturn(Optional.ofNullable(member));
+
+ reviewUpdateService.updateReview(review.getId(), request, authInfo);
+
+ assertAll(
+ () -> assertThat(review.getReviewTitle()).isEqualTo(request.getReviewTitle()),
+ () -> assertThat(review.getInstructorName()).isEqualTo(request.getInstructorName()),
+ () -> assertThat(review.getTakenSemyr()).isEqualTo(request.getTakenSemyr()),
+ () -> assertThat(review.getReviewContent()).isEqualTo(request.getReviewContent()),
+ () -> assertThat(review.getGrade()).isEqualTo(request.getGrade())
+ );
+ }
+
+ @Test
+ @DisplayName("권한이 없는 리뷰는 수정이 불가능하다.")
+ void updateReview_noAuth() {
+ ReviewUpdateRequest request = ReviewUpdateRequest.builder()
+ .build();
+
+ when(reviewRepository.findById(anyLong()))
+ .thenReturn(Optional.ofNullable(review));
+ when(memberRepository.findById(anyLong()))
+ .thenReturn(Optional.of(new Member(5L, "noAuth")));
+
+ assertThatThrownBy(() -> reviewUpdateService.updateReview(review.getId(), request, authInfo))
+ .isInstanceOf(AuthorizationException.class);
+ }
+
+}
\ No newline at end of file
diff --git a/src/test/java/org/wooriverygood/api/review/application/ReviewValidateAccessServiceTest.java b/src/test/java/org/wooriverygood/api/review/application/ReviewValidateAccessServiceTest.java
new file mode 100644
index 0000000..e389a88
--- /dev/null
+++ b/src/test/java/org/wooriverygood/api/review/application/ReviewValidateAccessServiceTest.java
@@ -0,0 +1,80 @@
+package org.wooriverygood.api.review.application;
+
+import org.junit.jupiter.api.DisplayName;
+import org.junit.jupiter.api.Test;
+import org.mockito.InjectMocks;
+import org.mockito.Mock;
+import org.wooriverygood.api.course.domain.Course;
+import org.wooriverygood.api.member.domain.Member;
+import org.wooriverygood.api.member.repository.MemberRepository;
+import org.wooriverygood.api.review.exception.ReviewAccessDeniedException;
+import org.wooriverygood.api.global.auth.AuthInfo;
+import org.wooriverygood.api.review.domain.Review;
+import org.wooriverygood.api.review.repository.ReviewRepository;
+import org.wooriverygood.api.util.MockTest;
+
+import java.time.LocalDateTime;
+import java.util.ArrayList;
+import java.util.Optional;
+
+import static org.assertj.core.api.Assertions.assertThatThrownBy;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyLong;
+import static org.mockito.Mockito.when;
+
+class ReviewValidateAccessServiceTest extends MockTest {
+
+ @InjectMocks
+ private ReviewValidateAccessService reviewValidateAccessService;
+
+ @Mock
+ private ReviewRepository reviewRepository;
+
+ @Mock
+ private MemberRepository memberRepository;
+
+
+ @Test
+ @DisplayName("마지막으로 작성한 리뷰가 현재 기준으로 6개월보다 가깝다면, 예외를 발생시키지 않는다.")
+ void canAccessReviews_true() {
+ Review review = Review.builder()
+ .createdAt(LocalDateTime.now().minusMonths(6))
+ .build();
+
+ when(memberRepository.findById(anyLong()))
+ .thenReturn(Optional.ofNullable(member));
+ when(reviewRepository.findTopByMemberOrderByCreatedAtDesc(any(Member.class)))
+ .thenReturn(Optional.ofNullable(review));
+
+ reviewValidateAccessService.validateReviewAccess(authInfo);
+ }
+
+ @Test
+ @DisplayName("리뷰를 작성하지 않았다면, 예외를 발생시킨다.")
+ void canAccessReviews_false_noReview() {
+ when(memberRepository.findById(anyLong()))
+ .thenReturn(Optional.ofNullable(member));
+ when(reviewRepository.findTopByMemberOrderByCreatedAtDesc(any(Member.class)))
+ .thenReturn(Optional.empty());
+
+ assertThatThrownBy(() -> reviewValidateAccessService.validateReviewAccess(authInfo))
+ .isInstanceOf(ReviewAccessDeniedException.class);
+ }
+
+ @Test
+ @DisplayName("마지막으로 작성한 리뷰가 현재 기준으로 6개월보다 멀다면, 예외를 발생시킨다.")
+ void canAccessReviews_false_sixMonths() {
+ Review review = Review.builder()
+ .createdAt(LocalDateTime.of(2022, 6, 13, 12, 00))
+ .build();
+
+ when(memberRepository.findById(anyLong()))
+ .thenReturn(Optional.ofNullable(member));
+ when(reviewRepository.findTopByMemberOrderByCreatedAtDesc(any(Member.class)))
+ .thenReturn(Optional.ofNullable(review));
+
+ assertThatThrownBy(() -> reviewValidateAccessService.validateReviewAccess(authInfo))
+ .isInstanceOf(ReviewAccessDeniedException.class);
+ }
+
+}
\ No newline at end of file
diff --git a/src/test/java/org/wooriverygood/api/review/service/ReviewServiceTest.java b/src/test/java/org/wooriverygood/api/review/service/ReviewServiceTest.java
deleted file mode 100644
index 4930a18..0000000
--- a/src/test/java/org/wooriverygood/api/review/service/ReviewServiceTest.java
+++ /dev/null
@@ -1,288 +0,0 @@
-package org.wooriverygood.api.review.service;
-
-import org.assertj.core.api.Assertions;
-import org.junit.jupiter.api.BeforeEach;
-import org.junit.jupiter.api.DisplayName;
-import org.junit.jupiter.api.Test;
-import org.junit.jupiter.api.extension.ExtendWith;
-import org.mockito.InjectMocks;
-import org.mockito.Mock;
-import org.mockito.Mockito;
-import org.mockito.junit.jupiter.MockitoExtension;
-import org.wooriverygood.api.advice.exception.AuthorizationException;
-import org.wooriverygood.api.advice.exception.CourseNotFoundException;
-import org.wooriverygood.api.course.domain.Courses;
-import org.wooriverygood.api.course.repository.CourseRepository;
-import org.wooriverygood.api.review.domain.Review;
-import org.wooriverygood.api.review.domain.ReviewLike;
-import org.wooriverygood.api.review.dto.*;
-import org.wooriverygood.api.review.repository.ReviewLikeRepository;
-import org.wooriverygood.api.review.repository.ReviewRepository;
-import org.wooriverygood.api.support.AuthInfo;
-
-import java.time.LocalDateTime;
-import java.util.ArrayList;
-import java.util.List;
-import java.util.Optional;
-
-import static org.mockito.ArgumentMatchers.any;
-
-@ExtendWith(MockitoExtension.class)
-public class ReviewServiceTest {
- @InjectMocks
- private ReviewService reviewService;
-
- @Mock
- private ReviewRepository reviewRepository;
-
- @Mock
- private CourseRepository courseRepository;
-
- @Mock
- private ReviewLikeRepository reviewLikeRepository;
- private final int REVIEW_COUNT = 10;
-
- List reviews = new ArrayList<>();
-
- Courses singleCourse= Courses.builder()
- .id(1L)
- .course_name("Gaoshu")
- .course_category("Zhuanye")
- .course_credit(5)
- .isYouguan(0)
- .kaikeYuanxi("Xinke")
- .build();
-
- AuthInfo authInfo = AuthInfo.builder()
- .sub("22222-34534-123")
- .username("22222-34534-123")
- .build();
-
- @BeforeEach
- void setUpReviews() {
- for(int i = 0; i())
- .updated(false)
- .build();
- reviews.add(review);
- }
- }
-
- Review singleReview = Review.builder()
- .id(1L)
- .course(singleCourse)
- .reviewContent("test review")
- .reviewTitle("test review title")
- .instructorName("jiaoshou")
- .takenSemyr("22-23")
- .grade("100")
- .authorEmail(authInfo.getUsername())
- .reviewLikes(new ArrayList<>())
- .updated(false)
- .createdAt(LocalDateTime.of(2022, 6, 13, 12, 00))
- .build();
-
- Review noAuthReview = Review.builder()
- .id(1L)
- .course(singleCourse)
- .reviewContent("test review")
- .reviewTitle("test review title")
- .instructorName("jiaoshou")
- .takenSemyr("22-23")
- .grade("100")
- .authorEmail("somerandom-username")
- .reviewLikes(new ArrayList<>())
- .updated(false)
- .createdAt(LocalDateTime.of(2024, 1, 13, 12, 00))
- .build();
-
- @Test
- @DisplayName("유효한 id를 통해 특정 수업의 리뷰들을 불러온다.")
- void findAllReviewsByCourseId() {
- Mockito.when(courseRepository.findById(any())).thenReturn(Optional.ofNullable(singleCourse));
- Mockito.when(reviewRepository.findAllByCourseId(any()))
- .thenReturn(reviews);
-
- List responses = reviewService.findAllReviewsByCourseId(1L, authInfo);
-
- Assertions.assertThat(responses).hasSize(REVIEW_COUNT);
- Assertions.assertThat(responses.get(0).getReview_title()).isEqualTo("review0");
-
- }
-
- @Test
- @DisplayName("존재하지 않는 강의의 리뷰는 불러올 수 없다.")
- void findAllReviewsByCourseId_noCourse() {
- Mockito.when(courseRepository.findById(any())).thenThrow(CourseNotFoundException.class);
-
- Assertions.assertThatThrownBy(() -> reviewService.findAllReviewsByCourseId(99L, authInfo))
- .isInstanceOf(CourseNotFoundException.class);
-
- }
-
-
- @Test
- @DisplayName("특정 강의의 리뷰를 작성한다.")
- void addReview() {
- NewReviewRequest newReviewRequest = NewReviewRequest.builder()
- .review_title("Test Review from TestCode")
- .instructor_name("Jiaoshou")
- .taken_semyr("1stSem")
- .review_content("Good!")
- .grade("100")
- .build();
-
- Mockito.when(reviewRepository.save(any(Review.class)))
- .thenReturn(Review.builder()
- .authorEmail(authInfo.getUsername())
- .reviewTitle(newReviewRequest.getReview_title())
- .instructorName(newReviewRequest.getInstructor_name())
- .takenSemyr(newReviewRequest.getTaken_semyr())
- .reviewContent(newReviewRequest.getReview_content())
- .grade(newReviewRequest.getGrade())
- .build());
-
- Mockito.when(courseRepository.findById(any()))
- .thenReturn(Optional.ofNullable(singleCourse));
-
- NewReviewResponse response = reviewService.addReview(authInfo, 1L, newReviewRequest);
-
- Assertions.assertThat(response.getAuthor_email()).isEqualTo(authInfo.getUsername());
- Assertions.assertThat(response.getReview_content()).isEqualTo(newReviewRequest.getReview_content());
- }
-
- @Test
- @DisplayName("특정 리뷰의 좋아요를 1 올린다.")
- void likeReview_up() {
- Mockito.when(reviewRepository.findById(any(Long.class)))
- .thenReturn(Optional.ofNullable(singleReview));
-
- ReviewLikeResponse response = reviewService.likeReview(singleReview.getId(), authInfo);
-
- Assertions.assertThat(response.getLike_count()).isEqualTo(singleReview.getLikeCount() + 1);
- Assertions.assertThat(response.isLiked()).isEqualTo(true);
- }
-
- @Test
- @DisplayName("특정 리뷰의 좋아요를 1 내린다.")
- void likeReview_down() {
- ReviewLike reviewLike = ReviewLike.builder()
- .id(3L)
- .review(singleReview)
- .username(authInfo.getUsername())
- .build();
-
- Mockito.when(reviewRepository.findById(any(Long.class)))
- .thenReturn(Optional.ofNullable(singleReview));
- Mockito.when(reviewLikeRepository.findByReviewAndUsername(any(Review.class), any(String.class)))
- .thenReturn(Optional.ofNullable(reviewLike));
-
- ReviewLikeResponse response = reviewService.likeReview(singleReview.getId(), authInfo);
-
- Assertions.assertThat(response.getLike_count()).isEqualTo(singleReview.getLikeCount() - 1);
- Assertions.assertThat(response.isLiked()).isEqualTo(false);
- }
-
- @Test
- @DisplayName("사용자 본인이 작성한 리뷰들을 불러온다.")
- void findMyReviews() {
- Mockito.when(reviewRepository.findByAuthorEmail(any(String.class)))
- .thenReturn(reviews);
-
- List responses = reviewService.findMyReviews(authInfo);
-
- Assertions.assertThat(responses.get(0).isMine()).isEqualTo(true);
- }
-
- @Test
- @DisplayName("권한이 있는 리뷰를 수정한다.")
- void updateReview() {
- ReviewUpdateRequest request = ReviewUpdateRequest.builder()
- .review_title("new title")
- .review_content("new content")
- .instructor_name("jiaoshou")
- .taken_semyr("18-19")
- .grade("100")
- .build();
-
- Mockito.when(reviewRepository.findById(any(Long.class)))
- .thenReturn(Optional.ofNullable(singleReview));
-
- ReviewUpdateResponse response = reviewService.updateReview(singleReview.getId(), request, authInfo);
-
- Assertions.assertThat(response.getReview_id()).isEqualTo(singleReview.getId());
- Assertions.assertThat(singleReview.isUpdated()).isEqualTo(true);
- }
-
-// @Test
-// @DisplayName("권한이 없는 리뷰는 수정이 불가능하다.")
-// void updateReview_noAuth() {
-// ReviewUpdateRequest request = ReviewUpdateRequest.builder()
-// .review_title("new title")
-// .review_content("new content")
-// .build();
-//
-// Mockito.when(reviewRepository.findById(any(Long.class)))
-// .thenReturn(Optional.ofNullable(noAuthReview));
-//
-// Assertions.assertThatThrownBy(() -> reviewService.updateReview(noAuthReview.getId(), request, authInfo))
-// .isInstanceOf(AuthorizationException.class);
-// }
-
- @Test
- @DisplayName("권한이 있는 리뷰를 삭제한다.")
- void deleteReview() {
- Mockito.when(reviewRepository.findById(any(Long.class)))
- .thenReturn(Optional.ofNullable(singleReview));
-
- ReviewDeleteResponse response = reviewService.deleteReview(singleReview.getId(), authInfo);
- Assertions.assertThat(response.getReview_id()).isEqualTo(singleReview.getId());
- }
-
- @Test
- @DisplayName("권한이 없는 리뷰는 삭제가 불가능하다.")
- void deleteReview_noAuth() {
- Mockito.when(reviewRepository.findById(any(Long.class)))
- .thenReturn(Optional.ofNullable(noAuthReview));
-
- Assertions.assertThatThrownBy(() -> reviewService.deleteReview(noAuthReview.getId(), authInfo))
- .isInstanceOf(AuthorizationException.class);
- }
-
- @Test
- @DisplayName("리뷰를 작성하지 않았다면, false를 반환한다.")
- void canAccessReviews_false_noReview() {
- Mockito.when(reviewRepository.findTopByAuthorEmailOrderByCreatedAtDesc(any(String.class)))
- .thenReturn(Optional.empty());
-
- Assertions.assertThat(reviewService.canAccessReviews(authInfo)).isEqualTo(false);
- }
-
- @Test
- @DisplayName("마지막으로 작성한 리뷰가 현재 기준으로 6개월보다 멀다면, false를 반환한다.")
- void canAccessReviews_false_sixMonths() {
- Mockito.when(reviewRepository.findTopByAuthorEmailOrderByCreatedAtDesc(any(String.class)))
- .thenReturn(Optional.ofNullable(singleReview));
-
- Assertions.assertThat(reviewService.canAccessReviews(authInfo)).isEqualTo(false);
- }
-
- @Test
- @DisplayName("마지막으로 작성한 리뷰가 현재 기준으로 6개월보다 가깝다면, true를 반환한다.")
- void canAccessReviews_true() {
- Mockito.when(reviewRepository.findTopByAuthorEmailOrderByCreatedAtDesc(any(String.class)))
- .thenReturn(Optional.ofNullable(noAuthReview));
-
- Assertions.assertThat(reviewService.canAccessReviews(authInfo)).isEqualTo(true);
- }
-
-}
diff --git a/src/test/java/org/wooriverygood/api/util/ControllerTest.java b/src/test/java/org/wooriverygood/api/util/ApiTest.java
similarity index 55%
rename from src/test/java/org/wooriverygood/api/util/ControllerTest.java
rename to src/test/java/org/wooriverygood/api/util/ApiTest.java
index 58ecb23..1ea00cc 100644
--- a/src/test/java/org/wooriverygood/api/util/ControllerTest.java
+++ b/src/test/java/org/wooriverygood/api/util/ApiTest.java
@@ -11,18 +11,21 @@
import org.springframework.security.test.context.support.WithMockUser;
import org.springframework.test.web.servlet.setup.MockMvcBuilders;
import org.springframework.web.context.WebApplicationContext;
-import org.wooriverygood.api.comment.controller.CommentController;
-import org.wooriverygood.api.comment.service.CommentService;
-import org.wooriverygood.api.report.controller.ReportController;
-import org.wooriverygood.api.report.service.ReportService;
-import org.wooriverygood.api.course.controller.CourseController;
-import org.wooriverygood.api.course.service.CourseService;
-import org.wooriverygood.api.post.controller.PostController;
-import org.wooriverygood.api.post.service.PostService;
-import org.wooriverygood.api.review.controller.ReviewController;
-import org.wooriverygood.api.review.service.ReviewService;
-import org.wooriverygood.api.support.AuthInfo;
-import org.wooriverygood.api.support.AuthenticationPrincipalArgumentResolver;
+import org.wooriverygood.api.comment.api.CommentApi;
+import org.wooriverygood.api.comment.application.*;
+import org.wooriverygood.api.course.application.CourseFindService;
+import org.wooriverygood.api.member.repository.MemberRepository;
+import org.wooriverygood.api.post.application.*;
+import org.wooriverygood.api.report.api.ReportApi;
+import org.wooriverygood.api.report.application.CommentReportService;
+import org.wooriverygood.api.course.api.CourseApi;
+import org.wooriverygood.api.course.application.CourseCreateService;
+import org.wooriverygood.api.post.api.PostApi;
+import org.wooriverygood.api.report.application.PostReportService;
+import org.wooriverygood.api.review.api.ReviewApi;
+import org.wooriverygood.api.review.application.*;
+import org.wooriverygood.api.global.auth.AuthInfo;
+import org.wooriverygood.api.global.auth.AuthenticationPrincipalArgumentResolver;
import static org.springframework.restdocs.mockmvc.MockMvcRestDocumentation.documentationConfiguration;
@@ -32,32 +35,80 @@
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*;
@WebMvcTest({
- CommentController.class,
- PostController.class,
- CourseController.class,
- ReviewController.class,
- ReportController.class
+ CommentApi.class,
+ PostApi.class,
+ CourseApi.class,
+ ReviewApi.class,
+ ReportApi.class
})
@WithMockUser
@ExtendWith(RestDocumentationExtension.class)
-public class ControllerTest {
+public class ApiTest {
protected MockMvcRequestSpecification restDocs;
@MockBean
- protected CourseService courseService;
+ protected MemberRepository memberRepository;
@MockBean
- protected ReviewService reviewService;
+ protected CourseCreateService courseCreateService;
@MockBean
- protected CommentService commentService;
+ protected CourseFindService courseFindService;
@MockBean
- protected PostService postService;
+ protected ReviewLikeToggleService reviewLikeToggleService;
@MockBean
- protected ReportService reportService;
+ protected ReviewFindService reviewFindService;
+
+ @MockBean
+ protected ReviewCreateService reviewCreateService;
+
+ @MockBean
+ protected ReviewDeleteService reviewDeleteService;
+
+ @MockBean
+ protected ReviewUpdateService reviewUpdateService;
+
+ @MockBean
+ protected ReviewValidateAccessService reviewValidateAccessService;
+
+ @MockBean
+ protected CommentCreateService commentCreateService;
+
+ @MockBean
+ protected CommentDeleteService commentDeleteService;
+
+ @MockBean
+ protected CommentFindService commentFindService;
+
+ @MockBean
+ protected CommentUpdateService commentUpdateService;
+
+ @MockBean
+ protected CommentLikeToggleService commentLikeToggleService;
+
+ @MockBean
+ protected PostLikeToggleService postLikeToggleService;
+
+ @MockBean
+ protected PostFindService postFindService;
+
+ @MockBean
+ protected PostCreateService postCreateService;
+
+ @MockBean
+ protected PostUpdateService postUpdateService;
+
+ @MockBean
+ protected PostDeleteService postDeleteService;
+
+ @MockBean
+ protected CommentReportService commentReportService;
+
+ @MockBean
+ protected PostReportService postReportService;
@MockBean
protected AuthenticationPrincipalArgumentResolver authenticationPrincipalArgumentResolver;
diff --git a/src/test/java/org/wooriverygood/api/util/MockTest.java b/src/test/java/org/wooriverygood/api/util/MockTest.java
new file mode 100644
index 0000000..a4aae7c
--- /dev/null
+++ b/src/test/java/org/wooriverygood/api/util/MockTest.java
@@ -0,0 +1,26 @@
+package org.wooriverygood.api.util;
+
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.extension.ExtendWith;
+import org.mockito.junit.jupiter.MockitoExtension;
+import org.wooriverygood.api.global.auth.AuthInfo;
+import org.wooriverygood.api.member.domain.Member;
+
+@ExtendWith(MockitoExtension.class)
+public class MockTest {
+
+ protected AuthInfo authInfo = AuthInfo.builder()
+ .memberId(1L)
+ .sub("22222-34534-123")
+ .username("22222-34534-123")
+ .build();
+
+ protected Member member;
+
+
+ @BeforeEach
+ void mockData() {
+ member = new Member(1L, authInfo.getUsername());
+ }
+
+}
diff --git a/src/test/java/org/wooriverygood/api/util/ResponseFixture.java b/src/test/java/org/wooriverygood/api/util/ResponseFixture.java
index ffd921e..af0f2f8 100644
--- a/src/test/java/org/wooriverygood/api/util/ResponseFixture.java
+++ b/src/test/java/org/wooriverygood/api/util/ResponseFixture.java
@@ -2,7 +2,7 @@
import org.wooriverygood.api.post.dto.PostResponse;
import org.wooriverygood.api.post.dto.PostsResponse;
-import org.wooriverygood.api.support.AuthInfo;
+import org.wooriverygood.api.global.auth.AuthInfo;
import java.time.LocalDateTime;
import java.util.ArrayList;
@@ -21,17 +21,17 @@ public static PostResponse postResponse(long id, AuthInfo authInfo) {
public static PostResponse postResponse(long id, String category, AuthInfo authInfo) {
return PostResponse.builder()
- .post_id(id)
- .post_title("title_" + id)
- .post_category(category)
- .post_content("content_" + id)
- .post_author(authInfo.getUsername())
- .post_comments((int) (Math.random() * 100))
- .post_likes((int) (Math.random() * 100))
- .post_time(LocalDateTime.now())
+ .postId(id)
+ .postTitle("title_" + id)
+ .postCategory(category)
+ .postComments((int) (Math.random() * 100))
+ .postLikes((int) (Math.random() * 100))
+ .postTime(LocalDateTime.now())
.liked(id % 6 == 0)
.updated(id % 9 == 0)
.reported(false)
+ .memberId(1L)
+ .isMine(false)
.build();
}
@@ -45,17 +45,17 @@ public static PostResponse reportedPostResponse(long id, AuthInfo authInfo) {
public static PostResponse reportedPostResponse(long id, String category, AuthInfo authInfo) {
return PostResponse.builder()
- .post_id(id)
- .post_title(null)
- .post_category(category)
- .post_content(null)
- .post_author(authInfo.getUsername())
- .post_comments((int) (Math.random() * 100))
- .post_likes((int) (Math.random() * 100))
- .post_time(LocalDateTime.now())
+ .postId(id)
+ .postTitle(null)
+ .postCategory(category)
+ .postComments((int) (Math.random() * 100))
+ .postLikes((int) (Math.random() * 100))
+ .postTime(LocalDateTime.now())
.liked(id % 6 == 0)
.updated(id % 9 == 0)
.reported(true)
+ .memberId(1L)
+ .isMine(false)
.build();
}
@@ -74,19 +74,5 @@ public static PostsResponse postsResponse(int page, int totalCount, AuthInfo aut
.build();
}
- public static PostsResponse postsResponse(int page, int totalCount, String category, AuthInfo authInfo) {
- List responses = new ArrayList<>();
- int bound = Math.min(totalCount - page * 10 + 1, 10);
- int start = page * 10 + 1;
- for (int i = start; i < start + bound; i++) {
- if (i % 5 == 0) responses.add(reportedPostResponse(i, authInfo));
- else responses.add(postResponse(i, authInfo));
- }
- return PostsResponse.builder()
- .posts(responses)
- .totalPostCount(totalCount)
- .totalPageCount((int) Math.ceil((double) totalCount / 10))
- .build();
- }
}