From 599c0a6a2290df63918a10d28d44a525ddc4b4a7 Mon Sep 17 00:00:00 2001 From: JunHyeongChoi Date: Fri, 11 Oct 2024 15:14:50 +0900 Subject: [PATCH 1/2] =?UTF-8?q?feat:=20Swagger=20API=20=EB=AC=B8=EC=84=9C?= =?UTF-8?q?=EC=97=90=20=EC=84=A4=EB=AA=85=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../controller/CategoryController.java | 40 +++++++-- .../controller/CommentController.java | 17 +++- .../controller/DiaryController.java | 89 ++++++++++++++++--- .../controller/FileController.java | 44 +++++++-- .../controller/FriendController.java | 46 +++++++--- .../controller/FriendRequestController.java | 45 ++++++++-- .../controller/LikeController.java | 24 ++++- .../controller/MemberController.java | 61 +++++++++---- .../controller/NotificationController.java | 21 ++++- 9 files changed, 317 insertions(+), 70 deletions(-) diff --git a/src/main/java/com/potatocake/everymoment/controller/CategoryController.java b/src/main/java/com/potatocake/everymoment/controller/CategoryController.java index 36a19fa..528c594 100644 --- a/src/main/java/com/potatocake/everymoment/controller/CategoryController.java +++ b/src/main/java/com/potatocake/everymoment/controller/CategoryController.java @@ -5,6 +5,12 @@ import com.potatocake.everymoment.dto.response.CategoryResponse; import com.potatocake.everymoment.security.MemberDetails; import com.potatocake.everymoment.service.CategoryService; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.media.Content; +import io.swagger.v3.oas.annotations.media.Schema; +import io.swagger.v3.oas.annotations.responses.ApiResponse; +import io.swagger.v3.oas.annotations.tags.Tag; import jakarta.validation.Valid; import java.util.List; import lombok.RequiredArgsConstructor; @@ -19,6 +25,7 @@ import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; +@Tag(name = "Categories", description = "카테고리 관리 API") @RequiredArgsConstructor @RequestMapping("/api/categories") @RestController @@ -26,8 +33,11 @@ public class CategoryController { private final CategoryService categoryService; + @Operation(summary = "카테고리 목록 조회", description = "사용자의 카테고리 목록을 조회합니다.") + @ApiResponse(responseCode = "200", description = "카테고리 목록 조회 성공", content = @Content(schema = @Schema(implementation = CategoryResponse.class))) @GetMapping public ResponseEntity> getCategories( + @Parameter(description = "인증된 사용자 정보", hidden = true) @AuthenticationPrincipal MemberDetails memberDetails) { List categories = categoryService.getCategories(memberDetails.getId()); @@ -35,28 +45,44 @@ public ResponseEntity> getCategories( .body(SuccessResponse.ok(categories)); } + @Operation(summary = "카테고리 추가", description = "새로운 카테고리를 추가합니다.") + @ApiResponse(responseCode = "200", description = "카테고리 추가 성공") @PostMapping - public ResponseEntity addCategory(@RequestBody @Valid CategoryCreateRequest request, - @AuthenticationPrincipal MemberDetails memberDetails) { + public ResponseEntity addCategory( + @Parameter(description = "카테고리 생성 정보", required = true) + @RequestBody @Valid CategoryCreateRequest request, + @Parameter(description = "인증된 사용자 정보", hidden = true) + @AuthenticationPrincipal MemberDetails memberDetails) { categoryService.addCategory(memberDetails.getId(), request); return ResponseEntity.ok() .body(SuccessResponse.ok()); } + @Operation(summary = "카테고리 수정", description = "기존 카테고리를 수정합니다.") + @ApiResponse(responseCode = "200", description = "카테고리 수정 성공") @PatchMapping("/{categoryId}") - public ResponseEntity updateCategory(@PathVariable Long categoryId, - @RequestBody @Valid CategoryCreateRequest request, - @AuthenticationPrincipal MemberDetails memberDetails) { + public ResponseEntity updateCategory( + @Parameter(description = "수정할 카테고리 ID", required = true) + @PathVariable Long categoryId, + @Parameter(description = "카테고리 수정 정보", required = true) + @RequestBody @Valid CategoryCreateRequest request, + @Parameter(description = "인증된 사용자 정보", hidden = true) + @AuthenticationPrincipal MemberDetails memberDetails) { categoryService.updateCategory(categoryId, memberDetails.getId(), request); return ResponseEntity.ok() .body(SuccessResponse.ok()); } + @Operation(summary = "카테고리 삭제", description = "카테고리를 삭제합니다.") + @ApiResponse(responseCode = "200", description = "카테고리 삭제 성공") @DeleteMapping("/{categoryId}") - public ResponseEntity deleteCategory(@PathVariable Long categoryId, - @AuthenticationPrincipal MemberDetails memberDetails) { + public ResponseEntity deleteCategory( + @Parameter(description = "삭제할 카테고리 ID", required = true) + @PathVariable Long categoryId, + @Parameter(description = "인증된 사용자 정보", hidden = true) + @AuthenticationPrincipal MemberDetails memberDetails) { categoryService.deleteCategory(categoryId, memberDetails.getId()); return ResponseEntity.ok() diff --git a/src/main/java/com/potatocake/everymoment/controller/CommentController.java b/src/main/java/com/potatocake/everymoment/controller/CommentController.java index f462e1d..61a8295 100644 --- a/src/main/java/com/potatocake/everymoment/controller/CommentController.java +++ b/src/main/java/com/potatocake/everymoment/controller/CommentController.java @@ -4,8 +4,11 @@ import com.potatocake.everymoment.dto.request.CommentRequest; import com.potatocake.everymoment.security.MemberDetails; import com.potatocake.everymoment.service.CommentService; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.responses.ApiResponse; +import io.swagger.v3.oas.annotations.tags.Tag; import lombok.RequiredArgsConstructor; -import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; import org.springframework.security.core.annotation.AuthenticationPrincipal; import org.springframework.web.bind.annotation.DeleteMapping; @@ -15,6 +18,7 @@ import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; +@Tag(name = "Comments", description = "댓글 관리 API") @RequiredArgsConstructor @RestController @RequestMapping("/api/comments") @@ -22,11 +26,15 @@ public class CommentController { private final CommentService commentService; - //댓글 수정 + @Operation(summary = "댓글 수정", description = "기존 댓글을 수정합니다.") + @ApiResponse(responseCode = "200", description = "댓글 수정 성공") @PatchMapping("/{commentId}") public ResponseEntity> updateComment( + @Parameter(description = "인증된 사용자 정보", hidden = true) @AuthenticationPrincipal MemberDetails memberDetails, + @Parameter(description = "수정할 댓글 ID", required = true) @PathVariable Long commentId, + @Parameter(description = "댓글 수정 정보", required = true) @RequestBody CommentRequest commentRequest) { Long memberId = memberDetails.getId(); @@ -36,10 +44,13 @@ public ResponseEntity> updateComment( .body(SuccessResponse.ok()); } - //댓글 삭제 + @Operation(summary = "댓글 삭제", description = "댓글을 삭제합니다.") + @ApiResponse(responseCode = "200", description = "댓글 삭제 성공") @DeleteMapping("/{commentId}") public ResponseEntity> deleteComment( + @Parameter(description = "인증된 사용자 정보", hidden = true) @AuthenticationPrincipal MemberDetails memberDetails, + @Parameter(description = "삭제할 댓글 ID", required = true) @PathVariable Long commentId) { Long memberId = memberDetails.getId(); diff --git a/src/main/java/com/potatocake/everymoment/controller/DiaryController.java b/src/main/java/com/potatocake/everymoment/controller/DiaryController.java index 66047a9..8d52071 100644 --- a/src/main/java/com/potatocake/everymoment/controller/DiaryController.java +++ b/src/main/java/com/potatocake/everymoment/controller/DiaryController.java @@ -8,7 +8,6 @@ import com.potatocake.everymoment.dto.response.CommentsResponse; import com.potatocake.everymoment.dto.response.FriendDiariesResponse; import com.potatocake.everymoment.dto.response.FriendDiaryResponse; -import com.potatocake.everymoment.dto.response.MemberDetailResponse; import com.potatocake.everymoment.dto.response.MyDiariesResponse; import com.potatocake.everymoment.dto.response.MyDiaryResponse; import com.potatocake.everymoment.dto.response.NotificationResponse; @@ -16,10 +15,15 @@ import com.potatocake.everymoment.service.CommentService; import com.potatocake.everymoment.service.DiaryService; import com.potatocake.everymoment.service.FriendDiaryService; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.media.Content; +import io.swagger.v3.oas.annotations.media.Schema; +import io.swagger.v3.oas.annotations.responses.ApiResponse; +import io.swagger.v3.oas.annotations.tags.Tag; import java.time.LocalDate; import lombok.RequiredArgsConstructor; import org.springframework.format.annotation.DateTimeFormat; -import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; import org.springframework.security.core.annotation.AuthenticationPrincipal; import org.springframework.web.bind.annotation.DeleteMapping; @@ -32,6 +36,7 @@ import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; +@Tag(name = "Diaries", description = "일기 관리 API") @RequiredArgsConstructor @RestController @RequestMapping("/api/diaries") @@ -41,10 +46,13 @@ public class DiaryController { private final FriendDiaryService friendDiaryService; private final CommentService commentService; - //자동 일기 작성 + @Operation(summary = "자동 일기 작성", description = "자동으로 일기를 작성합니다.") + @ApiResponse(responseCode = "200", description = "자동 일기 작성 성공", content = @Content(schema = @Schema(implementation = NotificationResponse.class))) @PostMapping("/auto") public ResponseEntity> createDiaryAuto( + @Parameter(description = "인증된 사용자 정보", hidden = true) @AuthenticationPrincipal MemberDetails memberDetails, + @Parameter(description = "자동 일기 작성 정보", required = true) @RequestBody DiaryAutoCreateRequest diaryAutoCreateRequest) { Long memberId = memberDetails.getId(); @@ -54,10 +62,13 @@ public ResponseEntity> createDiaryAuto( .body(SuccessResponse.ok(response)); } - //수기 일기 작성 + @Operation(summary = "수기 일기 작성", description = "수동으로 일기를 작성합니다.") + @ApiResponse(responseCode = "200", description = "수기 일기 작성 성공") @PostMapping("/manual") public ResponseEntity> createDiaryManual( + @Parameter(description = "인증된 사용자 정보", hidden = true) @AuthenticationPrincipal MemberDetails memberDetails, + @Parameter(description = "수기 일기 작성 정보", required = true) @RequestBody DiaryManualCreateRequest diaryManualCreateRequest) { Long memberId = memberDetails.getId(); diaryService.createDiaryManual(memberId, diaryManualCreateRequest); @@ -66,18 +77,29 @@ public ResponseEntity> createDiaryManual( .body(SuccessResponse.ok()); } - //내 일기 전체 조회(타임라인) + @Operation(summary = "내 일기 전체 조회", description = "사용자의 모든 일기를 조회합니다. (타임라인)") + @ApiResponse(responseCode = "200", description = "내 일기 전체 조회 성공", content = @Content(schema = @Schema(implementation = MyDiariesResponse.class))) @GetMapping("/my") public ResponseEntity> getMyDiaries( + @Parameter(description = "인증된 사용자 정보", hidden = true) @AuthenticationPrincipal MemberDetails memberDetails, + @Parameter(description = "검색 키워드") @RequestParam(required = false) String keyword, + @Parameter(description = "이모지 필터") @RequestParam(required = false) String emoji, + @Parameter(description = "카테고리 ID") @RequestParam(required = false) Long category, + @Parameter(description = "특정 날짜") @RequestParam(required = false) @DateTimeFormat(iso = DateTimeFormat.ISO.DATE) LocalDate date, + @Parameter(description = "시작 날짜") @RequestParam(required = false) @DateTimeFormat(iso = DateTimeFormat.ISO.DATE) LocalDate from, + @Parameter(description = "종료 날짜") @RequestParam(required = false) @DateTimeFormat(iso = DateTimeFormat.ISO.DATE) LocalDate until, + @Parameter(description = "북마크 여부") @RequestParam(required = false) Boolean bookmark, + @Parameter(description = "페이지 키") @RequestParam(defaultValue = "0") int key, + @Parameter(description = "페이지 크기") @RequestParam(defaultValue = "10") int size ) { Long memberId = memberDetails.getId(); @@ -99,10 +121,13 @@ public ResponseEntity> getMyDiaries( .body(SuccessResponse.ok(response)); } - //내 일기 상세 조회 + @Operation(summary = "내 일기 상세 조회", description = "특정 일기의 상세 내용을 조회합니다.") + @ApiResponse(responseCode = "200", description = "내 일기 상세 조회 성공", content = @Content(schema = @Schema(implementation = MyDiaryResponse.class))) @GetMapping("/my/{diaryId}") public ResponseEntity> getMyDiary( + @Parameter(description = "인증된 사용자 정보", hidden = true) @AuthenticationPrincipal MemberDetails memberDetails, + @Parameter(description = "조회할 일기 ID", required = true) @PathVariable Long diaryId) { Long memberId = memberDetails.getId(); @@ -112,11 +137,15 @@ public ResponseEntity> getMyDiary( .body(SuccessResponse.ok(response)); } - //일기 수정 + @Operation(summary = "일기 수정", description = "기존 일기를 수정합니다.") + @ApiResponse(responseCode = "200", description = "일기 수정 성공") @PatchMapping("/{diaryId}") public ResponseEntity> updateDiary( + @Parameter(description = "인증된 사용자 정보", hidden = true) @AuthenticationPrincipal MemberDetails memberDetails, + @Parameter(description = "수정할 일기 ID", required = true) @PathVariable Long diaryId, + @Parameter(description = "일기 수정 정보", required = true) @RequestBody DiaryManualCreateRequest diaryManualCreateRequest) { Long memberId = memberDetails.getId(); @@ -126,10 +155,13 @@ public ResponseEntity> updateDiary( .body(SuccessResponse.ok()); } - //일기 삭제 + @Operation(summary = "일기 삭제", description = "일기를 삭제합니다.") + @ApiResponse(responseCode = "200", description = "일기 삭제 성공") @DeleteMapping("/{diaryId}") public ResponseEntity> deleteDiary( + @Parameter(description = "인증된 사용자 정보", hidden = true) @AuthenticationPrincipal MemberDetails memberDetails, + @Parameter(description = "삭제할 일기 ID", required = true) @PathVariable Long diaryId) { Long memberId = memberDetails.getId(); @@ -139,10 +171,13 @@ public ResponseEntity> deleteDiary( .body(SuccessResponse.ok()); } - //북마크 설정 토글 + @Operation(summary = "북마크 설정 토글", description = "일기의 북마크 상태를 토글합니다.") + @ApiResponse(responseCode = "200", description = "북마크 설정 토글 성공") @PatchMapping("/{diaryId}/bookmark") public ResponseEntity> toggleBookmark( + @Parameter(description = "인증된 사용자 정보", hidden = true) @AuthenticationPrincipal MemberDetails memberDetails, + @Parameter(description = "토글할 일기 ID", required = true) @PathVariable Long diaryId) { Long memberId = memberDetails.getId(); @@ -152,10 +187,13 @@ public ResponseEntity> toggleBookmark( .body(SuccessResponse.ok()); } - //공개 설정 토글 + @Operation(summary = "공개 설정 토글", description = "일기의 공개 상태를 토글합니다.") + @ApiResponse(responseCode = "200", description = "공개 설정 토글 성공") @PatchMapping("/{diaryId}/privacy") public ResponseEntity> togglePrivacy( + @Parameter(description = "인증된 사용자 정보", hidden = true) @AuthenticationPrincipal MemberDetails memberDetails, + @Parameter(description = "토글할 일기 ID", required = true) @PathVariable Long diaryId) { Long memberId = memberDetails.getId(); @@ -165,18 +203,29 @@ public ResponseEntity> togglePrivacy( .body(SuccessResponse.ok()); } - //전체 친구 일기 조회 + @Operation(summary = "전체 친구 일기 조회", description = "사용자 친구들의 모든 일기를 조회합니다.") + @ApiResponse(responseCode = "200", description = "친구 일기 전체 조회 성공", content = @Content(schema = @Schema(implementation = FriendDiariesResponse.class))) @GetMapping("/friend") public ResponseEntity> getFriendDiaries( + @Parameter(description = "인증된 사용자 정보", hidden = true) @AuthenticationPrincipal MemberDetails memberDetails, + @Parameter(description = "검색 키워드") @RequestParam(required = false) String keyword, + @Parameter(description = "이모지 필터") @RequestParam(required = false) String emoji, + @Parameter(description = "카테고리 ID") @RequestParam(required = false) Long category, + @Parameter(description = "특정 날짜") @RequestParam(required = false) @DateTimeFormat(iso = DateTimeFormat.ISO.DATE) LocalDate date, + @Parameter(description = "시작 날짜") @RequestParam(required = false) @DateTimeFormat(iso = DateTimeFormat.ISO.DATE) LocalDate from, + @Parameter(description = "종료 날짜") @RequestParam(required = false) @DateTimeFormat(iso = DateTimeFormat.ISO.DATE) LocalDate until, + @Parameter(description = "북마크 여부") @RequestParam(required = false) Boolean bookmark, + @Parameter(description = "페이지 키") @RequestParam(defaultValue = "0") int key, + @Parameter(description = "페이지 크기") @RequestParam(defaultValue = "10") int size ) { Long memberId = memberDetails.getId(); @@ -198,10 +247,13 @@ public ResponseEntity> getFriendDiaries( .body(SuccessResponse.ok(response)); } - //친구 일기 상제 조회 + @Operation(summary = "친구 일기 상세 조회", description = "특정 친구 일기의 상세 내용을 조회합니다.") + @ApiResponse(responseCode = "200", description = "친구 일기 상세 조회 성공", content = @Content(schema = @Schema(implementation = FriendDiaryResponse.class))) @GetMapping("/friend/{diaryId}") public ResponseEntity> getFriendDiary( + @Parameter(description = "인증된 사용자 정보", hidden = true) @AuthenticationPrincipal MemberDetails memberDetails, + @Parameter(description = "조회할 친구 일기 ID", required = true) @PathVariable Long diaryId) { Long memberId = memberDetails.getId(); @@ -211,11 +263,15 @@ public ResponseEntity> getFriendDiary( .body(SuccessResponse.ok(response)); } - //댓글 조회 + @Operation(summary = "댓글 조회", description = "특정 일기의 댓글을 조회합니다.") + @ApiResponse(responseCode = "200", description = "댓글 조회 성공", content = @Content(schema = @Schema(implementation = CommentsResponse.class))) @GetMapping("/{diaryId}/comments") public ResponseEntity> getComments( + @Parameter(description = "댓글을 조회할 일기 ID", required = true) @PathVariable Long diaryId, + @Parameter(description = "페이지 키") @RequestParam(defaultValue = "0") int key, + @Parameter(description = "페이지 크기") @RequestParam(defaultValue = "10") int size ) { CommentsResponse response = commentService.getComments(diaryId, key, size); @@ -224,11 +280,15 @@ public ResponseEntity> getComments( .body(SuccessResponse.ok(response)); } - //댓글 작성 + @Operation(summary = "댓글 작성", description = "특정 일기에 댓글을 작성합니다.") + @ApiResponse(responseCode = "200", description = "댓글 작성 성공") @PostMapping("/{diaryId}/comments") public ResponseEntity> createComment( + @Parameter(description = "인증된 사용자 정보", hidden = true) @AuthenticationPrincipal MemberDetails memberDetails, + @Parameter(description = "댓글을 작성할 일기 ID", required = true) @PathVariable Long diaryId, + @Parameter(description = "댓글 작성 정보", required = true) @RequestBody CommentRequest commentRequest) { Long memberId = memberDetails.getId(); @@ -237,4 +297,5 @@ public ResponseEntity> createComment( return ResponseEntity.ok() .body(SuccessResponse.ok()); } + } diff --git a/src/main/java/com/potatocake/everymoment/controller/FileController.java b/src/main/java/com/potatocake/everymoment/controller/FileController.java index e17bd70..cb62468 100644 --- a/src/main/java/com/potatocake/everymoment/controller/FileController.java +++ b/src/main/java/com/potatocake/everymoment/controller/FileController.java @@ -5,6 +5,12 @@ import com.potatocake.everymoment.dto.response.FileResponse; import com.potatocake.everymoment.security.MemberDetails; import com.potatocake.everymoment.service.FileService; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.media.Content; +import io.swagger.v3.oas.annotations.media.Schema; +import io.swagger.v3.oas.annotations.responses.ApiResponse; +import io.swagger.v3.oas.annotations.tags.Tag; import java.util.List; import lombok.RequiredArgsConstructor; import org.springframework.http.MediaType; @@ -19,6 +25,7 @@ import org.springframework.web.bind.annotation.RestController; import org.springframework.web.multipart.MultipartFile; +@Tag(name = "Files", description = "파일 관리 API") @RequiredArgsConstructor @RequestMapping("/api/diaries/{diaryId}/files") @RestController @@ -26,30 +33,49 @@ public class FileController { private final FileService fileService; + @Operation(summary = "파일 목록 조회", description = "특정 일기의 파일 목록을 조회합니다.") + @ApiResponse(responseCode = "200", description = "파일 목록 조회 성공", content = @Content(schema = @Schema(implementation = FileResponse.class))) @GetMapping - public ResponseEntity> getFiles(@PathVariable Long diaryId) { + public ResponseEntity> getFiles(@Parameter(description = "조회할 일기 ID", required = true) + @PathVariable Long diaryId) { List files = fileService.getFiles(diaryId); return ResponseEntity.ok() .body(SuccessResponse.ok(files)); } + @Operation(summary = "파일 업로드", description = "특정 일기에 파일을 업로드합니다.") + @ApiResponse(responseCode = "200", description = "파일 업로드 성공") @PostMapping(consumes = MediaType.MULTIPART_FORM_DATA_VALUE) - public ResponseEntity uploadFiles(@PathVariable Long diaryId, - @AuthenticationPrincipal MemberDetails memberDetails, - @RequestPart List files, - @RequestPart List info) { + public ResponseEntity uploadFiles( + @Parameter(description = "파일을 업로드할 일기 ID", required = true) + @PathVariable Long diaryId, + @Parameter(description = "인증된 사용자 정보", hidden = true) + @AuthenticationPrincipal MemberDetails memberDetails, + @Parameter(description = "업로드할 파일 목록", required = true) + @RequestPart List files, + @Parameter(description = "일기에 보일 파일 이름과 순서", required = true) + @RequestPart List info + ) { fileService.uploadFiles(diaryId, memberDetails.getId(), files, info); return ResponseEntity.ok() .body(SuccessResponse.ok()); } + @Operation(summary = "파일 수정", description = "특정 일기의 파일을 수정합니다.") + @ApiResponse(responseCode = "200", description = "파일 수정 성공") @PutMapping(consumes = MediaType.MULTIPART_FORM_DATA_VALUE) - public ResponseEntity updateFiles(@PathVariable Long diaryId, - @AuthenticationPrincipal MemberDetails memberDetails, - @RequestPart List files, - @RequestPart List info) { + public ResponseEntity updateFiles( + @Parameter(description = "파일을 수정할 일기 ID", required = true) + @PathVariable Long diaryId, + @Parameter(description = "인증된 사용자 정보", hidden = true) + @AuthenticationPrincipal MemberDetails memberDetails, + @Parameter(description = "수정할 파일 목록", required = true) + @RequestPart List files, + @Parameter(description = "일기에 보일 파일 이름과 순서", required = true) + @RequestPart List info + ) { fileService.updateFiles(diaryId, memberDetails.getId(), files, info); return ResponseEntity.ok() diff --git a/src/main/java/com/potatocake/everymoment/controller/FriendController.java b/src/main/java/com/potatocake/everymoment/controller/FriendController.java index 79772c9..c1644dc 100644 --- a/src/main/java/com/potatocake/everymoment/controller/FriendController.java +++ b/src/main/java/com/potatocake/everymoment/controller/FriendController.java @@ -3,12 +3,16 @@ import com.potatocake.everymoment.dto.SuccessResponse; import com.potatocake.everymoment.dto.response.FriendListResponse; import com.potatocake.everymoment.dto.response.OneFriendDiariesResponse; -import com.potatocake.everymoment.entity.Member; import com.potatocake.everymoment.security.MemberDetails; import com.potatocake.everymoment.service.FriendService; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.media.Content; +import io.swagger.v3.oas.annotations.media.Schema; +import io.swagger.v3.oas.annotations.responses.ApiResponse; +import io.swagger.v3.oas.annotations.tags.Tag; import java.time.LocalDate; import org.springframework.format.annotation.DateTimeFormat; -import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; import org.springframework.security.core.annotation.AuthenticationPrincipal; import org.springframework.web.bind.annotation.DeleteMapping; @@ -19,6 +23,7 @@ import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; +@Tag(name = "Friends", description = "친구 관리 API") @RestController @RequestMapping("/api/friends") public class FriendController { @@ -28,14 +33,21 @@ public FriendController(FriendService friendService) { this.friendService = friendService; } - //특정 친구 일기 전체 조회 + @Operation(summary = "특정 친구 일기 전체 조회", description = "특정 친구의 모든 일기를 조회합니다.") + @ApiResponse(responseCode = "200", description = "친구 일기 조회 성공", content = @Content(schema = @Schema(implementation = OneFriendDiariesResponse.class))) @GetMapping("/{friendId}/diaries") public ResponseEntity> getOneFriendDiaries( + @Parameter(description = "인증된 사용자 정보", hidden = true) @AuthenticationPrincipal MemberDetails memberDetails, + @Parameter(description = "조회할 친구 ID", required = true) @PathVariable Long friendId, + @Parameter(description = "조회할 날짜") @RequestParam(required = false) @DateTimeFormat(iso = DateTimeFormat.ISO.DATE) LocalDate date, + @Parameter(description = "페이지 키") @RequestParam(defaultValue = "0") int key, - @RequestParam(defaultValue = "10") int size) { + @Parameter(description = "페이지 크기") + @RequestParam(defaultValue = "10") int size + ) { Long memberId = memberDetails.getId(); OneFriendDiariesResponse response = friendService.OneFriendDiariesResponse(memberId, friendId, date, key, size); @@ -44,13 +56,19 @@ public ResponseEntity> getOneFriendDia .body(SuccessResponse.ok(response)); } - //내 친구 목록 조회 + @Operation(summary = "내 친구 목록 조회", description = "사용자의 친구 목록을 조회합니다.") + @ApiResponse(responseCode = "200", description = "친구 목록 조회 성공", content = @Content(schema = @Schema(implementation = FriendListResponse.class))) @GetMapping("/friends") public ResponseEntity> getFriendList( + @Parameter(description = "인증된 사용자 정보", hidden = true) @AuthenticationPrincipal MemberDetails memberDetails, + @Parameter(description = "검색할 친구 닉네임") @RequestParam(required = false) String nickname, + @Parameter(description = "페이지 키") @RequestParam(defaultValue = "0") int key, - @RequestParam(defaultValue = "10") int size) { + @Parameter(description = "페이지 크기") + @RequestParam(defaultValue = "10") int size + ) { Long memberId = memberDetails.getId(); FriendListResponse response = friendService.getFriendList(memberId, nickname, key, size); @@ -59,11 +77,15 @@ public ResponseEntity> getFriendList( .body(SuccessResponse.ok(response)); } - //내 친구 삭제 + @Operation(summary = "친구 삭제", description = "특정 친구를 삭제합니다.") + @ApiResponse(responseCode = "200", description = "친구 삭제 성공") @DeleteMapping("/{friendId}") public ResponseEntity> deleteFriend( + @Parameter(description = "인증된 사용자 정보", hidden = true) @AuthenticationPrincipal MemberDetails memberDetails, - @PathVariable Long friendId) { + @Parameter(description = "삭제할 친구 ID", required = true) + @PathVariable Long friendId + ) { Long memberId = memberDetails.getId(); friendService.deleteFriend(memberId, friendId); @@ -72,11 +94,15 @@ public ResponseEntity> deleteFriend( .body(SuccessResponse.ok()); } - //친한 친구 설정 + @Operation(summary = "친한 친구 설정", description = "특정 친구를 친한 친구로 설정하거나 해제합니다.") + @ApiResponse(responseCode = "200", description = "친한 친구 설정 성공") @PatchMapping("/{friendId}/bookmark") public ResponseEntity> toggleCloseFriend( + @Parameter(description = "인증된 사용자 정보", hidden = true) @AuthenticationPrincipal MemberDetails memberDetails, - @PathVariable Long friendId) { + @Parameter(description = "설정할 친구 ID", required = true) + @PathVariable Long friendId + ) { Long memberId = memberDetails.getId(); friendService.toggleCloseFriend(memberId, friendId); diff --git a/src/main/java/com/potatocake/everymoment/controller/FriendRequestController.java b/src/main/java/com/potatocake/everymoment/controller/FriendRequestController.java index f960fbc..407aafa 100644 --- a/src/main/java/com/potatocake/everymoment/controller/FriendRequestController.java +++ b/src/main/java/com/potatocake/everymoment/controller/FriendRequestController.java @@ -6,6 +6,12 @@ import com.potatocake.everymoment.exception.GlobalException; import com.potatocake.everymoment.security.MemberDetails; import com.potatocake.everymoment.service.FriendRequestService; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.media.Content; +import io.swagger.v3.oas.annotations.media.Schema; +import io.swagger.v3.oas.annotations.responses.ApiResponse; +import io.swagger.v3.oas.annotations.tags.Tag; import lombok.RequiredArgsConstructor; import org.springframework.http.ResponseEntity; import org.springframework.security.core.annotation.AuthenticationPrincipal; @@ -16,17 +22,24 @@ import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; +@Tag(name = "Friend Requests", description = "친구 요청 관리 API") @RequiredArgsConstructor @RestController public class FriendRequestController { private final FriendRequestService friendRequestService; + @Operation(summary = "친구 요청 목록 조회", description = "사용자에게 온 친구 요청 목록을 조회합니다.") + @ApiResponse(responseCode = "200", description = "친구 요청 목록 조회 성공", content = @Content(schema = @Schema(implementation = FriendRequestPageRequest.class))) @GetMapping("/api/friend-requests") public ResponseEntity> getFriendRequests( + @Parameter(description = "페이지 키") @RequestParam(required = false) Long key, + @Parameter(description = "페이지 크기") @RequestParam(defaultValue = "10") int size, - @AuthenticationPrincipal MemberDetails memberDetails) { + @Parameter(description = "인증된 사용자 정보", hidden = true) + @AuthenticationPrincipal MemberDetails memberDetails + ) { FriendRequestPageRequest friendRequests = friendRequestService.getFriendRequests(key, size, memberDetails.getId()); @@ -34,9 +47,15 @@ public ResponseEntity> getFriendReques .body(SuccessResponse.ok(friendRequests)); } + @Operation(summary = "친구 요청 보내기", description = "특정 사용자에게 친구 요청을 보냅니다.") + @ApiResponse(responseCode = "200", description = "친구 요청 전송 성공") @PostMapping("/api/members/{memberId}/friend-requests") - public ResponseEntity sendFriendRequest(@PathVariable Long memberId, - @AuthenticationPrincipal MemberDetails memberDetails) { + public ResponseEntity sendFriendRequest( + @Parameter(description = "친구 요청을 받을 사용자 ID", required = true) + @PathVariable Long memberId, + @Parameter(description = "인증된 사용자 정보", hidden = true) + @AuthenticationPrincipal MemberDetails memberDetails + ) { if (memberDetails.getId().equals(memberId)) { throw new GlobalException(ErrorCode.SELF_FRIEND_REQUEST); } @@ -47,18 +66,30 @@ public ResponseEntity sendFriendRequest(@PathVariable Long memb .body(SuccessResponse.ok()); } + @Operation(summary = "친구 요청 수락", description = "받은 친구 요청을 수락합니다.") + @ApiResponse(responseCode = "200", description = "친구 요청 수락 성공") @PostMapping("/api/friend-requests/{requestId}/accept") - public ResponseEntity acceptFriendRequest(@PathVariable Long requestId, - @AuthenticationPrincipal MemberDetails memberDetails) { + public ResponseEntity acceptFriendRequest( + @Parameter(description = "수락할 친구 요청 ID", required = true) + @PathVariable Long requestId, + @Parameter(description = "인증된 사용자 정보", hidden = true) + @AuthenticationPrincipal MemberDetails memberDetails + ) { friendRequestService.acceptFriendRequest(requestId, memberDetails.getId()); return ResponseEntity.ok() .body(SuccessResponse.ok()); } + @Operation(summary = "친구 요청 거절", description = "받은 친구 요청을 거절합니다.") + @ApiResponse(responseCode = "200", description = "친구 요청 거절 성공") @DeleteMapping("/api/friend-requests/{requestId}/reject") - public ResponseEntity rejectFriendRequest(@PathVariable Long requestId, - @AuthenticationPrincipal MemberDetails memberDetails) { + public ResponseEntity rejectFriendRequest( + @Parameter(description = "거절할 친구 요청 ID", required = true) + @PathVariable Long requestId, + @Parameter(description = "인증된 사용자 정보", hidden = true) + @AuthenticationPrincipal MemberDetails memberDetails + ) { friendRequestService.rejectFriendRequest(requestId, memberDetails.getId()); return ResponseEntity.ok() diff --git a/src/main/java/com/potatocake/everymoment/controller/LikeController.java b/src/main/java/com/potatocake/everymoment/controller/LikeController.java index 229497e..c6729b3 100644 --- a/src/main/java/com/potatocake/everymoment/controller/LikeController.java +++ b/src/main/java/com/potatocake/everymoment/controller/LikeController.java @@ -4,6 +4,12 @@ import com.potatocake.everymoment.dto.response.LikeCountResponse; import com.potatocake.everymoment.security.MemberDetails; import com.potatocake.everymoment.service.LikeService; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.media.Content; +import io.swagger.v3.oas.annotations.media.Schema; +import io.swagger.v3.oas.annotations.responses.ApiResponse; +import io.swagger.v3.oas.annotations.tags.Tag; import lombok.RequiredArgsConstructor; import org.springframework.http.ResponseEntity; import org.springframework.security.core.annotation.AuthenticationPrincipal; @@ -13,6 +19,7 @@ import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; +@Tag(name = "Likes", description = "좋아요 관리 API") @RequiredArgsConstructor @RequestMapping("/api/diaries/{diaryId}/likes") @RestController @@ -20,17 +27,28 @@ public class LikeController { private final LikeService likeService; + @Operation(summary = "좋아요 수 조회", description = "특정 일기의 좋아요 수를 조회합니다.") + @ApiResponse(responseCode = "200", description = "좋아요 수 조회 성공", content = @Content(schema = @Schema(implementation = LikeCountResponse.class))) @GetMapping - public ResponseEntity> getLikeCount(@PathVariable Long diaryId) { + public ResponseEntity> getLikeCount( + @Parameter(description = "조회할 일기 ID", required = true) + @PathVariable Long diaryId + ) { LikeCountResponse likeCount = likeService.getLikeCount(diaryId); return ResponseEntity.ok() .body(SuccessResponse.ok(likeCount)); } + @Operation(summary = "좋아요 토글", description = "특정 일기에 대한 좋아요를 추가하거나 취소합니다.") + @ApiResponse(responseCode = "200", description = "좋아요 토글 성공") @PostMapping - public ResponseEntity toggleLike(@PathVariable Long diaryId, - @AuthenticationPrincipal MemberDetails memberDetails) { + public ResponseEntity toggleLike( + @Parameter(description = "토글할 일기 ID", required = true) + @PathVariable Long diaryId, + @Parameter(description = "인증된 사용자 정보", hidden = true) + @AuthenticationPrincipal MemberDetails memberDetails + ) { likeService.toggleLike(memberDetails.getId(), diaryId); return ResponseEntity.ok() diff --git a/src/main/java/com/potatocake/everymoment/controller/MemberController.java b/src/main/java/com/potatocake/everymoment/controller/MemberController.java index b94885f..894c9d6 100644 --- a/src/main/java/com/potatocake/everymoment/controller/MemberController.java +++ b/src/main/java/com/potatocake/everymoment/controller/MemberController.java @@ -11,6 +11,11 @@ import com.potatocake.everymoment.security.MemberDetails; import com.potatocake.everymoment.service.MemberService; import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.media.Content; +import io.swagger.v3.oas.annotations.media.Schema; +import io.swagger.v3.oas.annotations.responses.ApiResponse; +import io.swagger.v3.oas.annotations.tags.Tag; import lombok.RequiredArgsConstructor; import org.springframework.http.MediaType; import org.springframework.http.ResponseEntity; @@ -26,6 +31,7 @@ import org.springframework.web.bind.annotation.RestController; import org.springframework.web.multipart.MultipartFile; +@Tag(name = "Members", description = "회원 관리 API") @RequiredArgsConstructor @RequestMapping("/api/members") @RestController @@ -34,53 +40,73 @@ public class MemberController { private final MemberService memberService; @Operation(summary = "로그인", description = "회원 번호와 닉네임으로 로그인합니다.") -// @ApiResponses(value = { -// @ApiResponse(responseCode = "200", description = "로그인 성공", -// content = @Content(schema = @Schema(implementation = JwtResponse.class))), -// @ApiResponse(responseCode = "401", description = "로그인 실패", -// content = @Content(schema = @Schema(implementation = ErrorResponse.class))) -// }) + @ApiResponse(responseCode = "200", description = "로그인 성공", content = @Content(schema = @Schema(implementation = JwtResponse.class))) @PostMapping("/login") - public ResponseEntity> login(@RequestBody MemberLoginRequest request) { + public ResponseEntity> login( + @Parameter(description = "로그인 정보", required = true) + @RequestBody MemberLoginRequest request + ) { // 이 메서드는 실제로 호출되지 않습니다. Swagger 문서화를 위해서만 존재합니다. // 실제 로그인 처리는 LoginFilter에서 이루어집니다. return ResponseEntity.ok(SuccessResponse.ok(JwtResponse.of("token"))); } + @Operation(summary = "회원 검색", description = "닉네임으로 회원을 검색합니다.") + @ApiResponse(responseCode = "200", description = "회원 검색 성공", content = @Content(schema = @Schema(implementation = MemberSearchResponse.class))) @GetMapping public ResponseEntity> searchMembers( + @Parameter(description = "검색할 닉네임") @RequestParam(required = false) String nickname, + @Parameter(description = "페이지 키") @RequestParam(required = false) Long key, + @Parameter(description = "페이지 크기") @RequestParam(defaultValue = "10") int size, - @AuthenticationPrincipal MemberDetails memberDetails) { - + @Parameter(description = "인증된 사용자 정보", hidden = true) + @AuthenticationPrincipal MemberDetails memberDetails + ) { MemberSearchResponse response = memberService.searchMembers(nickname, key, size, memberDetails.getId()); return ResponseEntity.ok() .body(SuccessResponse.ok(response)); } + @Operation(summary = "내 정보 조회", description = "로그인한 회원의 상세 정보를 조회합니다.") + @ApiResponse(responseCode = "200", description = "내 정보 조회 성공", content = @Content(schema = @Schema(implementation = MemberDetailResponse.class))) @GetMapping("/me") public ResponseEntity> myInfo( - @AuthenticationPrincipal MemberDetails memberDetails) { + @Parameter(description = "인증된 사용자 정보", hidden = true) + @AuthenticationPrincipal MemberDetails memberDetails + ) { MemberDetailResponse response = memberService.getMyInfo(memberDetails.getId()); return ResponseEntity.ok() .body(SuccessResponse.ok(response)); } + @Operation(summary = "회원 정보 조회", description = "특정 회원의 정보를 조회합니다.") + @ApiResponse(responseCode = "200", description = "회원 정보 조회 성공", content = @Content(schema = @Schema(implementation = MemberMyResponse.class))) @GetMapping("/{memberId}") - public ResponseEntity> memberInfo(@PathVariable Long memberId) { + public ResponseEntity> memberInfo( + @Parameter(description = "조회할 회원 ID", required = true) + @PathVariable Long memberId + ) { MemberMyResponse response = memberService.getMemberInfo(memberId); return ResponseEntity.ok() .body(SuccessResponse.ok(response)); } + @Operation(summary = "회원 정보 수정", description = "로그인한 회원의 프로필 이미지와 닉네임을 수정합니다.") + @ApiResponse(responseCode = "200", description = "회원 정보 수정 성공") @PostMapping(consumes = MediaType.MULTIPART_FORM_DATA_VALUE) - public ResponseEntity> updateMemberInfo(@AuthenticationPrincipal MemberDetails memberDetails, - @RequestParam(required = false) MultipartFile profileImage, - @RequestParam(required = false) String nickname) { + public ResponseEntity> updateMemberInfo( + @Parameter(description = "인증된 사용자 정보", hidden = true) + @AuthenticationPrincipal MemberDetails memberDetails, + @Parameter(description = "프로필 이미지 파일") + @RequestParam(required = false) MultipartFile profileImage, + @Parameter(description = "새로운 닉네임") + @RequestParam(required = false) String nickname + ) { validateProfileUpdate(profileImage, nickname); memberService.updateMemberInfo(memberDetails.getId(), profileImage, nickname); @@ -89,8 +115,13 @@ public ResponseEntity> updateMemberInfo(@AuthenticationPri .body(SuccessResponse.ok()); } + @Operation(summary = "회원 탈퇴", description = "로그인한 회원의 계정을 삭제합니다.") + @ApiResponse(responseCode = "200", description = "회원 탈퇴 성공") @DeleteMapping - public ResponseEntity> deleteMember(@AuthenticationPrincipal MemberDetails memberDetails) { + public ResponseEntity> deleteMember( + @Parameter(description = "인증된 사용자 정보", hidden = true) + @AuthenticationPrincipal MemberDetails memberDetails + ) { memberService.deleteMember(memberDetails.getId()); return ResponseEntity.ok() diff --git a/src/main/java/com/potatocake/everymoment/controller/NotificationController.java b/src/main/java/com/potatocake/everymoment/controller/NotificationController.java index e8ce3d9..ccd186f 100644 --- a/src/main/java/com/potatocake/everymoment/controller/NotificationController.java +++ b/src/main/java/com/potatocake/everymoment/controller/NotificationController.java @@ -4,6 +4,12 @@ import com.potatocake.everymoment.dto.response.NotificationListResponse; import com.potatocake.everymoment.security.MemberDetails; import com.potatocake.everymoment.service.NotificationService; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.media.Content; +import io.swagger.v3.oas.annotations.media.Schema; +import io.swagger.v3.oas.annotations.responses.ApiResponse; +import io.swagger.v3.oas.annotations.tags.Tag; import java.util.List; import lombok.RequiredArgsConstructor; import org.springframework.http.ResponseEntity; @@ -14,6 +20,7 @@ import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; +@Tag(name = "Notifications", description = "알림 관리 API") @RequiredArgsConstructor @RestController @RequestMapping("/api/notifications") @@ -21,9 +28,13 @@ public class NotificationController { private final NotificationService notificationService; + @Operation(summary = "알림 목록 조회", description = "로그인한 사용자의 알림 목록을 조회합니다.") + @ApiResponse(responseCode = "200", description = "알림 목록 조회 성공", content = @Content(schema = @Schema(implementation = NotificationListResponse.class))) @GetMapping public ResponseEntity>> getNotifications( - @AuthenticationPrincipal MemberDetails memberDetails){ + @Parameter(description = "인증된 사용자 정보", hidden = true) + @AuthenticationPrincipal MemberDetails memberDetails + ) { Long memberId = memberDetails.getId(); List response = notificationService.getNotifications(memberId); @@ -32,10 +43,15 @@ public ResponseEntity>> getNotifi .body(SuccessResponse.ok(response)); } + @Operation(summary = "알림 읽음 처리", description = "특정 알림을 읽음 처리합니다.") + @ApiResponse(responseCode = "200", description = "알림 읽음 처리 성공") @PatchMapping("/{notificationId}") public ResponseEntity> updateNotification( + @Parameter(description = "인증된 사용자 정보", hidden = true) @AuthenticationPrincipal MemberDetails memberDetails, - @PathVariable Long notificationId) { + @Parameter(description = "읽음 처리할 알림 ID", required = true) + @PathVariable Long notificationId + ) { Long memberId = memberDetails.getId(); notificationService.updateNotification(memberId, notificationId); @@ -43,4 +59,5 @@ public ResponseEntity> updateNotification( return ResponseEntity.ok() .body(SuccessResponse.ok()); } + } From 8c05137cfbf3b6f5a02d327235441856f307e2d1 Mon Sep 17 00:00:00 2001 From: JunHyeongChoi Date: Fri, 11 Oct 2024 15:20:51 +0900 Subject: [PATCH 2/2] =?UTF-8?q?feat:=20=ED=95=9C=EA=B5=AD=20=EC=8B=9C?= =?UTF-8?q?=EA=B0=84=EB=8C=80=EB=A1=9C=20=EC=84=A4=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/master_weekly_cicd.yml | 2 +- src/main/resources/application-prod.yml | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/.github/workflows/master_weekly_cicd.yml b/.github/workflows/master_weekly_cicd.yml index 1c4ad9e..1e8ae40 100644 --- a/.github/workflows/master_weekly_cicd.yml +++ b/.github/workflows/master_weekly_cicd.yml @@ -56,4 +56,4 @@ jobs: script: | cd ./project sudo fuser -k -n tcp 8080 || true - nohup java -jar app.jar > ./output.log 2>&1 & + nohup java -Duser.timezone=Asia/Seoul -jar app.jar > ./output.log 2>&1 & diff --git a/src/main/resources/application-prod.yml b/src/main/resources/application-prod.yml index 3fc83ae..62053b5 100644 --- a/src/main/resources/application-prod.yml +++ b/src/main/resources/application-prod.yml @@ -8,6 +8,8 @@ spring: properties: hibernate: format_sql: true + jdbc: + time_zone: Asia/Seoul show-sql: true h2: