diff --git a/src/main/java/org/ai/roboadvisor/domain/community/controller/BoardController.java b/src/main/java/org/ai/roboadvisor/domain/community/controller/BoardController.java new file mode 100644 index 0000000..d0e0906 --- /dev/null +++ b/src/main/java/org/ai/roboadvisor/domain/community/controller/BoardController.java @@ -0,0 +1,50 @@ +package org.ai.roboadvisor.domain.community.controller; + +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.tags.Tag; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.ai.roboadvisor.domain.community.dto.response.BoardResponse; +import org.ai.roboadvisor.domain.community.service.BoardService; +import org.ai.roboadvisor.domain.community.swagger_annotation.board.getAllPostsByType.*; +import org.ai.roboadvisor.domain.tendency.entity.Tendency; +import org.ai.roboadvisor.global.common.dto.SuccessApiResponse; +import org.ai.roboadvisor.global.exception.SuccessCode; +import org.ai.roboadvisor.global.swagger_annotation.ApiResponse_Internal_Server_Error; +import org.springframework.data.domain.Pageable; +import org.springframework.data.domain.Sort; +import org.springframework.data.web.PageableDefault; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.RestController; + +import java.util.List; + +@Slf4j +@RequiredArgsConstructor +@Tag(name = "community] board", description = "게시글 전체 불러오기 API") +@RestController +@RequestMapping("/api/community/board") +public class BoardController { + + private final BoardService boardService; + + private final int PAGE_SIZE = 10; + + @Operation(summary = "게시글 전체 조회", description = "게시글 전체 불러오기 API") + @getAllPostsByType_OK + @getAllPostsByType_BAD_REQUEST + @ApiResponse_Internal_Server_Error + @GetMapping() + public ResponseEntity>> getAllPostsByType( + @RequestParam("tendency") Tendency tendency, @PageableDefault(size = PAGE_SIZE, sort = {"id"}, + direction = Sort.Direction.ASC) Pageable pageable) { + return ResponseEntity.status(HttpStatus.OK) + .body(SuccessApiResponse.success(SuccessCode.BOARD_ALL_VIEW_SUCCESS, + boardService.getAllPostsByType(tendency, pageable))); + } + +} diff --git a/src/main/java/org/ai/roboadvisor/domain/community/controller/CommentController.java b/src/main/java/org/ai/roboadvisor/domain/community/controller/CommentController.java new file mode 100644 index 0000000..d859e89 --- /dev/null +++ b/src/main/java/org/ai/roboadvisor/domain/community/controller/CommentController.java @@ -0,0 +1,69 @@ +package org.ai.roboadvisor.domain.community.controller; + +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.tags.Tag; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.ai.roboadvisor.domain.community.dto.request.CommentDeleteRequest; +import org.ai.roboadvisor.domain.community.dto.request.CommentRequest; +import org.ai.roboadvisor.domain.community.dto.request.CommentUpdateRequest; +import org.ai.roboadvisor.domain.community.dto.response.CommentResponse; +import org.ai.roboadvisor.domain.community.service.CommentService; +import org.ai.roboadvisor.domain.community.swagger_annotation.comment.delete.*; +import org.ai.roboadvisor.domain.community.swagger_annotation.comment.save.*; +import org.ai.roboadvisor.domain.community.swagger_annotation.comment.update.*; +import org.ai.roboadvisor.global.common.dto.SuccessApiResponse; +import org.ai.roboadvisor.global.exception.SuccessCode; +import org.ai.roboadvisor.global.swagger_annotation.ApiResponse_Internal_Server_Error; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.*; + +@Slf4j +@RequiredArgsConstructor +@Tag(name = "community] comment", description = "댓글 작성, 수정, 삭제 API") +@RestController +@RequestMapping("/api/community/comment") +public class CommentController { + + private final CommentService commentService; + + @Operation(summary = "댓글 작성", description = "댓글 작성 API") + @save_CREATED + @save_BAD_REQUEST + @save_UNAUTHORIZED + @ApiResponse_Internal_Server_Error + @PostMapping("/{postId}") + public ResponseEntity> save(@PathVariable("postId") Long postId, + @RequestBody CommentRequest commentRequest) { + return ResponseEntity.status(HttpStatus.CREATED) + .body(SuccessApiResponse.success(SuccessCode.COMMENT_CREATED_SUCCESS, + commentService.save(postId, commentRequest))); + } + + @Operation(summary = "댓글 수정", description = "댓글 수정 API") + @update_OK + @update_BAD_REQUEST + @update_UNAUTHORIZED + @ApiResponse_Internal_Server_Error + @PutMapping("/{postId}") + public ResponseEntity> update(@PathVariable("postId") Long postId, + @RequestBody CommentUpdateRequest commentUpdateRequest) { + return ResponseEntity.status(HttpStatus.OK) + .body(SuccessApiResponse.success(SuccessCode.COMMENT_UPDATE_SUCCESS, + commentService.update(postId, commentUpdateRequest))); + } + + @Operation(summary = "댓글 삭제", description = "댓글 삭제 API") + @delete_OK + @delete_BAD_REQUEST + @delete_UNAUTHORIZED + @ApiResponse_Internal_Server_Error + @DeleteMapping("/{postId}") + public ResponseEntity> delete(@PathVariable("postId") Long postId, + @RequestBody CommentDeleteRequest commentDeleteRequest) { + return ResponseEntity.status(HttpStatus.OK) + .body(SuccessApiResponse.success(SuccessCode.COMMENT_DELETE_SUCCESS, + commentService.delete(postId, commentDeleteRequest))); + } +} diff --git a/src/main/java/org/ai/roboadvisor/domain/community/controller/PostController.java b/src/main/java/org/ai/roboadvisor/domain/community/controller/PostController.java index 7c0daf6..66dfaad 100644 --- a/src/main/java/org/ai/roboadvisor/domain/community/controller/PostController.java +++ b/src/main/java/org/ai/roboadvisor/domain/community/controller/PostController.java @@ -1,73 +1,40 @@ package org.ai.roboadvisor.domain.community.controller; import io.swagger.v3.oas.annotations.Operation; -import io.swagger.v3.oas.annotations.media.Content; -import io.swagger.v3.oas.annotations.media.ExampleObject; -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 lombok.extern.slf4j.Slf4j; +import org.ai.roboadvisor.domain.community.dto.request.PostDeleteRequest; import org.ai.roboadvisor.domain.community.dto.request.PostRequest; import org.ai.roboadvisor.domain.community.dto.response.PostResponse; import org.ai.roboadvisor.domain.community.service.PostService; +import org.ai.roboadvisor.domain.community.swagger_annotation.post.delete.delete_OK; +import org.ai.roboadvisor.domain.community.swagger_annotation.post.delete.delete_UNAUTHORIZED; +import org.ai.roboadvisor.domain.community.swagger_annotation.post.getPostById.getPostById_BAD_REQUEST; +import org.ai.roboadvisor.domain.community.swagger_annotation.post.getPostById.getPostById_OK; +import org.ai.roboadvisor.domain.community.swagger_annotation.post.save.save_BAD_REQUEST; +import org.ai.roboadvisor.domain.community.swagger_annotation.post.save.save_CREATED; +import org.ai.roboadvisor.domain.community.swagger_annotation.post.update.update_BAD_REQUEST; +import org.ai.roboadvisor.domain.community.swagger_annotation.post.update.update_OK; +import org.ai.roboadvisor.domain.community.swagger_annotation.post.update.update_UNAUTHORIZED; import org.ai.roboadvisor.global.common.dto.SuccessApiResponse; -import org.ai.roboadvisor.global.exception.CustomException; -import org.ai.roboadvisor.global.exception.ErrorCode; import org.ai.roboadvisor.global.exception.SuccessCode; import org.ai.roboadvisor.global.swagger_annotation.ApiResponse_Internal_Server_Error; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.*; -import static org.ai.roboadvisor.global.exception.ErrorIntValue.*; - @Slf4j @RequiredArgsConstructor -@Tag(name = "community] post", description = "게시글 작성, 수정, 삭제 API") +@Tag(name = "community] post", description = "게시글 CRUD API") @RestController @RequestMapping("/api/community/post") public class PostController { private final PostService postService; @Operation(summary = "게시글 작성", description = "게시글 작성 API") - @ApiResponse(responseCode = "201", description = """ - 정상 응답. data로 게시글 정보를 리턴한다. - - id: 게시글 고유 번호(식별 번호), tendency: 투자 성향, nickname: 게시글 작성자 닉네임, - - content: 게시글 작성 내용, time: 게시글 작성 시간, viewcount: 조회수 - """, - content = @Content(schema = @Schema(implementation = SuccessApiResponse.class), - examples = @ExampleObject(name = "example", - description = "정상 응답 예시", - value = """ - { - "code": 201, - "message": "게시글이 정상적으로 등록되었습니다", - "data": { - "id": 1, - "tendency": "SHEEP", - "nickname": "testUser", - "content": "안녕하세요", - "time": "2023-09-21 01:06:19", - "viewCount": 0 - } - } - """ - ))) - @ApiResponse(responseCode = "400", description = "투자 성향이 잘못 입력된 경우", - content = @Content(schema = @Schema(implementation = SuccessApiResponse.class), - examples = @ExampleObject(name = "example", - description = "투자 성향이 잘못 입력된 경우 예시", - value = """ - { - "code": 400, - "message": "잘못된 투자 성향 형식이 입력되었습니다", - "data": null - } - """ - ))) + @save_CREATED + @save_BAD_REQUEST @ApiResponse_Internal_Server_Error @PostMapping() public ResponseEntity> save(@RequestBody PostRequest postRequest) { @@ -76,54 +43,21 @@ public ResponseEntity> save(@RequestBody PostRe postService.save(postRequest))); } + @Operation(summary = "게시글 조회", description = "게시글 조회 API") + @getPostById_OK + @getPostById_BAD_REQUEST + @ApiResponse_Internal_Server_Error + @GetMapping("/{postId}") + public ResponseEntity> getPostById(@PathVariable("postId") Long postId) { + return ResponseEntity.status(HttpStatus.OK) + .body(SuccessApiResponse.success(SuccessCode.POST_VIEW_SUCCESS, + postService.getPostById(postId))); + } + @Operation(summary = "게시글 수정", description = "게시글 수정 API") - @ApiResponse(responseCode = "200", description = """ - 정상 응답 - - 게시글이 정상적으로 수정된 경우: 응답 객체는 '게시글 작성' 과 동일하다. - """, - content = @Content(schema = @Schema(implementation = SuccessApiResponse.class), - examples = @ExampleObject(name = "example", - description = "정상 응답 예시", - value = """ - { - "code": 200, - "message": "게시글 수정이 정상적으로 처리되었습니다", - "data": { - "id": 1, - "tendency": "LION", - "nickname": "testUser", - "content": "안녕하세요3333", - "time": "2023-09-21 01:06:20", - "viewCount": 0 - } - } - """ - ))) - @ApiResponse(responseCode = "400", description = "투자 성향이 잘못 입력된 경우", - content = @Content(schema = @Schema(implementation = SuccessApiResponse.class), - examples = @ExampleObject(name = "example", - description = "투자 성향이 잘못 입력된 경우 예시", - value = """ - { - "code": 400, - "message": "잘못된 투자 성향 형식이 입력되었습니다", - "data": null - } - """ - ))) - @ApiResponse(responseCode = "401", description = "게시글 수정 권한이 없는 경우", - content = @Content(schema = @Schema(implementation = SuccessApiResponse.class), - examples = @ExampleObject(name = "example", - description = "게시글 수정 권한이 없는 경우 예시", - value = """ - { - "code": 401, - "message": "게시글 수정 혹은 삭제 권한이 존재하지 않습니다", - "data": null - } - """ - ))) + @update_OK + @update_BAD_REQUEST + @update_UNAUTHORIZED @ApiResponse_Internal_Server_Error @PutMapping("/{postId}") public ResponseEntity> update(@PathVariable("postId") Long postId, @RequestBody PostRequest postRequest) { @@ -133,44 +67,15 @@ public ResponseEntity> update(@PathVariable("po } @Operation(summary = "게시글 삭제", description = "게시글 삭제 API") - @ApiResponse(responseCode = "200", description = """ - 정상 응답 - - 게시글이 정상적으로 삭제된 경우 - """, - content = @Content(schema = @Schema(implementation = SuccessApiResponse.class), - examples = @ExampleObject(name = "example", - description = "정상 응답 예시", - value = """ - { - "code": 200, - "message": "게시글 삭제가 정상적으로 처리되었습니다", - "data": null - } - """ - ))) - @ApiResponse(responseCode = "401", description = "게시글 삭제 권한이 없는 경우", - content = @Content(schema = @Schema(implementation = SuccessApiResponse.class), - examples = @ExampleObject(name = "example", - description = "게시글 삭제 권한이 없는 예시", - value = """ - { - "code": 401, - "message": "게시글 수정 혹은 삭제 권한이 존재하지 않습니다", - "data": null - } - """ - ))) + @delete_OK + @delete_UNAUTHORIZED @ApiResponse_Internal_Server_Error @DeleteMapping("/{postId}") - public ResponseEntity> delete(@PathVariable("postId") Long postId, @RequestBody PostRequest postRequest) { - int result = postService.delete(postId, postRequest); - if (result == SUCCESS.getValue()) { - return ResponseEntity.status(HttpStatus.OK) - .body(SuccessApiResponse.success(SuccessCode.POST_DELETE_SUCCESS)); - } else { - throw new CustomException(ErrorCode.INTERNAL_SERVER_ERROR); - } + public ResponseEntity> delete(@PathVariable("postId") Long postId, + @RequestBody PostDeleteRequest postDeleteRequest) { + return ResponseEntity.status(HttpStatus.OK) + .body(SuccessApiResponse.success(SuccessCode.POST_DELETE_SUCCESS, + postService.delete(postId, postDeleteRequest))); } } diff --git a/src/main/java/org/ai/roboadvisor/domain/community/dto/CommentDto.java b/src/main/java/org/ai/roboadvisor/domain/community/dto/CommentDto.java new file mode 100644 index 0000000..5f4671e --- /dev/null +++ b/src/main/java/org/ai/roboadvisor/domain/community/dto/CommentDto.java @@ -0,0 +1,26 @@ +package org.ai.roboadvisor.domain.community.dto; + +import com.fasterxml.jackson.annotation.JsonFormat; +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.NoArgsConstructor; +import org.ai.roboadvisor.domain.community.entity.Comment; + +import java.time.LocalDateTime; + +@Getter +@AllArgsConstructor +public class CommentDto { + + private Long id; + private String nickname; + private String content; + + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "Asia/Seoul") + private LocalDateTime createdDateTime; + + public static CommentDto fromComment(Comment comment) { + return new CommentDto(comment.getId(), comment.getNickname(), + comment.getContent(), comment.getCreatedDateTime()); + } +} diff --git a/src/main/java/org/ai/roboadvisor/domain/community/dto/request/CommentDeleteRequest.java b/src/main/java/org/ai/roboadvisor/domain/community/dto/request/CommentDeleteRequest.java new file mode 100644 index 0000000..5076ff2 --- /dev/null +++ b/src/main/java/org/ai/roboadvisor/domain/community/dto/request/CommentDeleteRequest.java @@ -0,0 +1,21 @@ +package org.ai.roboadvisor.domain.community.dto.request; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.NoArgsConstructor; + +import javax.validation.constraints.NotBlank; + +@Getter +@NoArgsConstructor +@AllArgsConstructor +public class CommentDeleteRequest { + + @Schema(description = "댓글 번호", example = "1") + private Long commentId; + + @Schema(description = "사용자의 닉네임", example = "testUser") + @NotBlank + private String nickname; +} diff --git a/src/main/java/org/ai/roboadvisor/domain/community/dto/request/CommentRequest.java b/src/main/java/org/ai/roboadvisor/domain/community/dto/request/CommentRequest.java new file mode 100644 index 0000000..57b4705 --- /dev/null +++ b/src/main/java/org/ai/roboadvisor/domain/community/dto/request/CommentRequest.java @@ -0,0 +1,37 @@ +package org.ai.roboadvisor.domain.community.dto.request; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.NoArgsConstructor; +import org.ai.roboadvisor.domain.community.entity.Comment; +import org.ai.roboadvisor.domain.community.entity.Post; +import org.ai.roboadvisor.domain.tendency.entity.Tendency; + +import javax.validation.constraints.NotBlank; + +@Getter +@NoArgsConstructor +@AllArgsConstructor +public class CommentRequest { + + @Schema(description = "사용자의 투자 성향", example = "SHEEP") + @NotBlank + private Tendency tendency; + + @Schema(description = "사용자의 닉네임", example = "testUser") + @NotBlank + private String nickname; + + @Schema(description = "댓글 내용", example = "안녕하세요. 댓글 작성 1입니다. ") + @NotBlank + private String content; + + public static Comment fromCommentRequest(CommentRequest commentRequest, Post post) { + return Comment.builder() + .post(post) + .nickname(commentRequest.getNickname()) + .content(commentRequest.getContent()) + .build(); + } +} diff --git a/src/main/java/org/ai/roboadvisor/domain/community/dto/request/CommentUpdateRequest.java b/src/main/java/org/ai/roboadvisor/domain/community/dto/request/CommentUpdateRequest.java new file mode 100644 index 0000000..925ff6e --- /dev/null +++ b/src/main/java/org/ai/roboadvisor/domain/community/dto/request/CommentUpdateRequest.java @@ -0,0 +1,25 @@ +package org.ai.roboadvisor.domain.community.dto.request; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.NoArgsConstructor; + +import javax.validation.constraints.NotBlank; + +@Getter +@NoArgsConstructor +@AllArgsConstructor +public class CommentUpdateRequest { + + @Schema(description = "댓글 번호", example = "1") + private Long commentId; + + @Schema(description = "사용자의 닉네임", example = "testUser") + @NotBlank + private String nickname; + + @Schema(description = "댓글 내용", example = "안녕하세요. 댓글 작성 1입니다. ") + @NotBlank + private String content; +} diff --git a/src/main/java/org/ai/roboadvisor/domain/community/dto/request/PostDeleteRequest.java b/src/main/java/org/ai/roboadvisor/domain/community/dto/request/PostDeleteRequest.java new file mode 100644 index 0000000..94dafbe --- /dev/null +++ b/src/main/java/org/ai/roboadvisor/domain/community/dto/request/PostDeleteRequest.java @@ -0,0 +1,18 @@ +package org.ai.roboadvisor.domain.community.dto.request; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.NoArgsConstructor; + +import javax.validation.constraints.NotBlank; + +@Getter +@NoArgsConstructor +@AllArgsConstructor +public class PostDeleteRequest { + + @Schema(description = "사용자의 닉네임", example = "testUser") + @NotBlank + private String nickname; +} diff --git a/src/main/java/org/ai/roboadvisor/domain/community/dto/response/BoardResponse.java b/src/main/java/org/ai/roboadvisor/domain/community/dto/response/BoardResponse.java new file mode 100644 index 0000000..9cc49ec --- /dev/null +++ b/src/main/java/org/ai/roboadvisor/domain/community/dto/response/BoardResponse.java @@ -0,0 +1,30 @@ +package org.ai.roboadvisor.domain.community.dto.response; + +import com.fasterxml.jackson.annotation.JsonFormat; +import lombok.AllArgsConstructor; +import lombok.Getter; +import org.ai.roboadvisor.domain.community.entity.Post; +import org.ai.roboadvisor.domain.tendency.entity.Tendency; + +import java.time.LocalDateTime; + +@Getter +@AllArgsConstructor +public class BoardResponse { + + private Long id; + private Tendency tendency; + private String nickname; + private String content; + + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "Asia/Seoul") + private LocalDateTime createdDateTime; + + private Long viewCount; + private int commentCount; // number of comments in a post + + public static BoardResponse fromPostAndCommentCount(Post post, int commentCount) { + return new BoardResponse(post.getId(), post.getTendency(), post.getNickname(), + post.getContent(), post.getCreatedDateTime(), post.getViewCount(), commentCount); + } +} diff --git a/src/main/java/org/ai/roboadvisor/domain/community/dto/response/CommentResponse.java b/src/main/java/org/ai/roboadvisor/domain/community/dto/response/CommentResponse.java new file mode 100644 index 0000000..73e29e8 --- /dev/null +++ b/src/main/java/org/ai/roboadvisor/domain/community/dto/response/CommentResponse.java @@ -0,0 +1,31 @@ +package org.ai.roboadvisor.domain.community.dto.response; + +import com.fasterxml.jackson.annotation.JsonFormat; +import lombok.AllArgsConstructor; +import lombok.Getter; +import org.ai.roboadvisor.domain.community.entity.Comment; + +import java.time.LocalDateTime; + +@Getter +@AllArgsConstructor +public class CommentResponse { + + private Long id; + private Long postId; // 게시글 번호 + private String nickname; + private String content; + + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "Asia/Seoul") + private LocalDateTime createdDateTime; + + public static CommentResponse of(Long id, Long postId, String nickname, String content, LocalDateTime createdDateTime) { + return new CommentResponse(id, postId, nickname, content, createdDateTime); + } + + public static CommentResponse fromCommentEntity(Comment comment) { + return new CommentResponse(comment.getId(), comment.getPost().getId(), + comment.getNickname(), comment.getContent(), comment.getCreatedDateTime()); + } + +} diff --git a/src/main/java/org/ai/roboadvisor/domain/community/dto/response/PostResponse.java b/src/main/java/org/ai/roboadvisor/domain/community/dto/response/PostResponse.java index d7fec83..50e682f 100644 --- a/src/main/java/org/ai/roboadvisor/domain/community/dto/response/PostResponse.java +++ b/src/main/java/org/ai/roboadvisor/domain/community/dto/response/PostResponse.java @@ -3,10 +3,14 @@ import com.fasterxml.jackson.annotation.JsonFormat; import lombok.AllArgsConstructor; import lombok.Getter; +import org.ai.roboadvisor.domain.community.dto.CommentDto; +import org.ai.roboadvisor.domain.community.entity.DeleteStatus; import org.ai.roboadvisor.domain.community.entity.Post; import org.ai.roboadvisor.domain.tendency.entity.Tendency; import java.time.LocalDateTime; +import java.util.List; +import java.util.stream.Collectors; @Getter @AllArgsConstructor @@ -18,17 +22,22 @@ public class PostResponse { private String content; @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "Asia/Seoul") - private LocalDateTime time; + private LocalDateTime createdDateTime; private Long viewCount; - public static PostResponse of(Long id, Tendency tendency, String nickname, String content, LocalDateTime time, - Long viewCount) { - return new PostResponse(id, tendency, nickname, content, time, viewCount); - } + private List comments; public static PostResponse fromPostEntity(Post post) { + // 연관관계를 맺은 엔티티간의 무한 참조를 방지하기 위해 DTO 객체를 사용 + List commentDtos = post.getComments() + .stream() + .filter(comment -> (comment.getDeleteStatus() == DeleteStatus.F)) + .map(CommentDto::fromComment) + .collect(Collectors.toList()); + return new PostResponse(post.getId(), post.getTendency(), - post.getNickname(), post.getContent(), post.getCreatedDateTime(), post.getViewCount()); + post.getNickname(), post.getContent(), post.getCreatedDateTime(), + post.getViewCount(), commentDtos); } } diff --git a/src/main/java/org/ai/roboadvisor/domain/community/entity/Comment.java b/src/main/java/org/ai/roboadvisor/domain/community/entity/Comment.java new file mode 100644 index 0000000..597d8f3 --- /dev/null +++ b/src/main/java/org/ai/roboadvisor/domain/community/entity/Comment.java @@ -0,0 +1,61 @@ +package org.ai.roboadvisor.domain.community.entity; + +import lombok.AccessLevel; +import lombok.Builder; +import lombok.Getter; +import lombok.NoArgsConstructor; + +import javax.persistence.*; + +@Getter +@NoArgsConstructor(access = AccessLevel.PROTECTED) // 인자 없는 기본 생성자 필요 +@Entity +@Table(name = "comments") +public class Comment extends CommentBaseTimeEntity { + + // deleteStatus is initialized before the entity is persisted: + @PrePersist + private void prePersist() { + if (deleteStatus == null) { + deleteStatus = DeleteStatus.F; + } + } + + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + @Column(name = "cmt_id") + private Long id; + + @Column(name = "cmt_nickname", nullable = false, length = 50) + private String nickname; + + @Column(name = "cmt_content", nullable = false, columnDefinition = "TEXT") + private String content; + + @Enumerated(EnumType.STRING) + @Column(name = "cmt_delete_status", nullable = false, + columnDefinition = "ENUM('T', 'F') DEFAULT 'F'") + private DeleteStatus deleteStatus; + + @ManyToOne(fetch = FetchType.LAZY) + @JoinColumn(name = "post_id") + private Post post; + + @Builder + private Comment(Long id, String nickname, String content, + DeleteStatus deleteStatus, Post post) { + this.id = id; + this.nickname = nickname; + this.content = content; + this.deleteStatus = deleteStatus; + this.post = post; + } + + public void setContent(String content) { + this.content = content; + } + + public void setDeleteStatus(DeleteStatus deleteStatus) { + this.deleteStatus = deleteStatus; + } +} diff --git a/src/main/java/org/ai/roboadvisor/domain/community/entity/CommentBaseTimeEntity.java b/src/main/java/org/ai/roboadvisor/domain/community/entity/CommentBaseTimeEntity.java new file mode 100644 index 0000000..6a45159 --- /dev/null +++ b/src/main/java/org/ai/roboadvisor/domain/community/entity/CommentBaseTimeEntity.java @@ -0,0 +1,26 @@ +package org.ai.roboadvisor.domain.community.entity; + +import lombok.Getter; +import org.springframework.data.annotation.CreatedDate; +import org.springframework.data.annotation.LastModifiedDate; +import org.springframework.data.jpa.domain.support.AuditingEntityListener; + +import javax.persistence.Column; +import javax.persistence.EntityListeners; +import javax.persistence.MappedSuperclass; +import java.time.LocalDateTime; + +@Getter +@MappedSuperclass +// When JPA Entity class extends BaseTimeEntity class, files(createdDate, etc) are recognized as column +@EntityListeners(AuditingEntityListener.class) +public abstract class CommentBaseTimeEntity { + + @CreatedDate + @Column(name = "cmt_created_at") + private LocalDateTime createdDateTime; + + @LastModifiedDate + @Column(name = "cmt_modified_at") + private LocalDateTime modifiedDateTime; +} \ No newline at end of file diff --git a/src/main/java/org/ai/roboadvisor/domain/community/entity/DeleteStatus.java b/src/main/java/org/ai/roboadvisor/domain/community/entity/DeleteStatus.java new file mode 100644 index 0000000..0afd077 --- /dev/null +++ b/src/main/java/org/ai/roboadvisor/domain/community/entity/DeleteStatus.java @@ -0,0 +1,11 @@ +package org.ai.roboadvisor.domain.community.entity; + +public enum DeleteStatus { + /** + * Used in Post, Comment entity + * T: Delete is True + * F: Delete is False + * Default: F + */ + T, F +} diff --git a/src/main/java/org/ai/roboadvisor/domain/community/entity/Post.java b/src/main/java/org/ai/roboadvisor/domain/community/entity/Post.java index 5b056c4..5d9c5c9 100644 --- a/src/main/java/org/ai/roboadvisor/domain/community/entity/Post.java +++ b/src/main/java/org/ai/roboadvisor/domain/community/entity/Post.java @@ -7,6 +7,7 @@ import org.ai.roboadvisor.domain.tendency.entity.Tendency; import javax.persistence.*; +import java.util.List; @Getter @NoArgsConstructor(access = AccessLevel.PROTECTED) // 인자 없는 기본 생성자 필요 @@ -14,6 +15,14 @@ @Table(name = "posts") public class Post extends BaseTimeEntity { + // // deleteStatus is initialized before the entity is persisted: + @PrePersist + private void prePersist() { + if (deleteStatus == null) { + deleteStatus = DeleteStatus.F; + } + } + @Id @GeneratedValue(strategy = GenerationType.IDENTITY) @Column(name = "post_id") @@ -32,6 +41,17 @@ public class Post extends BaseTimeEntity { @Column(name = "post_view_count", columnDefinition = "INTEGER DEFAULT 0") private Long viewCount; + @Enumerated(EnumType.STRING) + @Column(name = "post_delete_status", nullable = false, + columnDefinition = "ENUM('T', 'F') DEFAULT 'F'") + private DeleteStatus deleteStatus; + + // 게시글 UI에서 댓글을 바로 보여주기 위해 FetchType.EAGER 설정 + // (댓글-펼처보기 와 같은 형식이면 LAZY로) + @OneToMany(mappedBy = "post", cascade = CascadeType.REMOVE, fetch = FetchType.EAGER) + @OrderBy("id asc") // 댓글 정렬 + private List comments; + @Builder private Post(Tendency tendency, String nickname, String content, Long viewCount) { this.tendency = tendency; @@ -56,4 +76,7 @@ public void setViewCount(Long viewCount) { this.viewCount = viewCount; } + public void setDeleteStatus(DeleteStatus deleteStatus) { + this.deleteStatus = deleteStatus; + } } diff --git a/src/main/java/org/ai/roboadvisor/domain/community/repository/CommentRepository.java b/src/main/java/org/ai/roboadvisor/domain/community/repository/CommentRepository.java new file mode 100644 index 0000000..1ece723 --- /dev/null +++ b/src/main/java/org/ai/roboadvisor/domain/community/repository/CommentRepository.java @@ -0,0 +1,20 @@ +package org.ai.roboadvisor.domain.community.repository; + +import org.ai.roboadvisor.domain.community.entity.Comment; +import org.ai.roboadvisor.domain.community.entity.DeleteStatus; +import org.ai.roboadvisor.domain.community.entity.Post; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.jpa.repository.Modifying; +import org.springframework.data.jpa.repository.Query; +import org.springframework.data.repository.query.Param; + +import java.util.Optional; + +public interface CommentRepository extends JpaRepository { + Optional findCommentByIdAndPost(Long id, Post post); + + @Modifying + @Query("UPDATE Comment c SET c.deleteStatus = :deleteStatus WHERE c.post.id = :postId") + void markCommentsAsDeleted(@Param("postId") Long postId, @Param("deleteStatus") DeleteStatus deleteStatus); + +} diff --git a/src/main/java/org/ai/roboadvisor/domain/community/repository/PostRepository.java b/src/main/java/org/ai/roboadvisor/domain/community/repository/PostRepository.java index 8638eaa..ef7d17d 100644 --- a/src/main/java/org/ai/roboadvisor/domain/community/repository/PostRepository.java +++ b/src/main/java/org/ai/roboadvisor/domain/community/repository/PostRepository.java @@ -2,14 +2,19 @@ import org.ai.roboadvisor.domain.community.entity.Post; import org.ai.roboadvisor.domain.tendency.entity.Tendency; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.Pageable; import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.jpa.repository.Query; +import org.springframework.data.repository.query.Param; -import java.util.List; import java.util.Optional; public interface PostRepository extends JpaRepository { - Optional findPostById(Long id); + @Query("SELECT p FROM Post p WHERE p.id = :id AND p.deleteStatus = 'F'") + Optional findPostById(@Param("id") Long id); - List findAllByTendencyOrderByIdDesc(Tendency tendency); + @Query("SELECT p FROM Post p WHERE p.tendency = :tendency AND p.deleteStatus = 'F'") + Page findPostsByTendencyAndDeleteStatusIsFalse(@Param("tendency") Tendency tendency, Pageable pageable); } diff --git a/src/main/java/org/ai/roboadvisor/domain/community/service/BoardService.java b/src/main/java/org/ai/roboadvisor/domain/community/service/BoardService.java new file mode 100644 index 0000000..78f31ec --- /dev/null +++ b/src/main/java/org/ai/roboadvisor/domain/community/service/BoardService.java @@ -0,0 +1,44 @@ +package org.ai.roboadvisor.domain.community.service; + +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.ai.roboadvisor.domain.community.dto.response.BoardResponse; +import org.ai.roboadvisor.domain.community.entity.Post; +import org.ai.roboadvisor.domain.community.repository.PostRepository; +import org.ai.roboadvisor.domain.tendency.entity.Tendency; +import org.ai.roboadvisor.global.exception.CustomException; +import org.ai.roboadvisor.global.exception.ErrorCode; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.Pageable; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.util.List; +import java.util.stream.Collectors; + +@Slf4j +@RequiredArgsConstructor +@Service +public class BoardService { + + private final PostRepository postRepository; + + @Transactional(readOnly = true) + public List getAllPostsByType(Tendency tendency, Pageable pageable) { + Page posts = postRepository.findPostsByTendencyAndDeleteStatusIsFalse(tendency, pageable); + + return posts.stream() + .map(p -> { + int commentCnt = findCommentsByPostId(p.getId()); + return BoardResponse.fromPostAndCommentCount(p, commentCnt); + }) + .collect(Collectors.toList()); + } + + public int findCommentsByPostId(Long postId) { + Post post = postRepository.findPostById(postId) + .orElseThrow(() -> new CustomException(ErrorCode.POST_ID_NOT_EXISTS)); + return post.getComments().size(); + } + +} diff --git a/src/main/java/org/ai/roboadvisor/domain/community/service/CommentService.java b/src/main/java/org/ai/roboadvisor/domain/community/service/CommentService.java new file mode 100644 index 0000000..c2c6c0e --- /dev/null +++ b/src/main/java/org/ai/roboadvisor/domain/community/service/CommentService.java @@ -0,0 +1,113 @@ +package org.ai.roboadvisor.domain.community.service; + +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.ai.roboadvisor.domain.community.dto.request.CommentDeleteRequest; +import org.ai.roboadvisor.domain.community.dto.request.CommentRequest; +import org.ai.roboadvisor.domain.community.dto.request.CommentUpdateRequest; +import org.ai.roboadvisor.domain.community.dto.response.CommentResponse; +import org.ai.roboadvisor.domain.community.entity.Comment; +import org.ai.roboadvisor.domain.community.entity.DeleteStatus; +import org.ai.roboadvisor.domain.community.entity.Post; +import org.ai.roboadvisor.domain.community.repository.CommentRepository; +import org.ai.roboadvisor.domain.community.repository.PostRepository; +import org.ai.roboadvisor.domain.tendency.entity.Tendency; +import org.ai.roboadvisor.global.exception.CustomException; +import org.ai.roboadvisor.global.exception.ErrorCode; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +@Slf4j +@RequiredArgsConstructor +@Service +public class CommentService { + + private final PostRepository postRepository; + private final CommentRepository commentRepository; + + @Transactional + public CommentResponse save(Long postId, CommentRequest commentRequest) { + Tendency commentTendency = commentRequest.getTendency(); + checkTendencyIsValid(commentTendency); + + Post existPost = postRepository.findPostById(postId) + .orElseThrow(() -> new CustomException(ErrorCode.POST_ID_NOT_EXISTS)); + checkCommentIsValid(commentTendency, existPost); + + Comment newComment = CommentRequest.fromCommentRequest(commentRequest, existPost); + + // save + saveComment(newComment); + return CommentResponse.fromCommentEntity(newComment); + } + + @Transactional + public CommentResponse update(Long postId, CommentUpdateRequest commentUpdateRequest) { + Comment existingComment = findExistingCommentById(postId, commentUpdateRequest.getCommentId()); + + // validate if user has authority + validateUserHasAuthority(commentUpdateRequest.getNickname(), existingComment); + + // update + updateCommentEntity(existingComment, commentUpdateRequest); + + return CommentResponse.fromCommentEntity(existingComment); + } + + @Transactional + public CommentResponse delete(Long postId, CommentDeleteRequest commentDeleteRequest) { + Comment existingComment = findExistingCommentById(postId, commentDeleteRequest.getCommentId()); + + // validate if user has authority + validateUserHasAuthority(commentDeleteRequest.getNickname(), existingComment); + + // manually delete a comment + deleteComment(existingComment); + + return CommentResponse.fromCommentEntity(existingComment); + } + + private void checkTendencyIsValid(Tendency tendency) { + if (tendency == Tendency.TYPE_NOT_EXISTS) { + throw new CustomException(ErrorCode.TENDENCY_INPUT_INVALID); + } + } + + private void checkCommentIsValid(Tendency tendency, Post post) { + // check tendency of post and comment is the same + if (tendency != post.getTendency()) { + throw new CustomException(ErrorCode.TENDENCY_NOT_MATCH_BETWEEN_POST_AND_COMMENT); + } + } + + private void saveComment(Comment comment) { + try { + commentRepository.save(comment); + } catch (Exception e) { + log.error("Save error: ", e); + throw new CustomException(ErrorCode.INTERNAL_SERVER_ERROR); + } + } + + private Comment findExistingCommentById(Long postId, Long commentId) { + Post existPost = postRepository.findPostById(postId) + .orElseThrow(() -> new CustomException(ErrorCode.POST_ID_NOT_EXISTS)); + return commentRepository.findCommentByIdAndPost(commentId, existPost) + .orElseThrow(() -> new CustomException(ErrorCode.INTERNAL_SERVER_ERROR)); + } + + private void validateUserHasAuthority(String requestNickname, Comment existingComment) { + if (!(requestNickname.equals(existingComment.getNickname()))) { + throw new CustomException(ErrorCode.USER_HAS_NOT_AUTHORIZED); + } + } + + private void updateCommentEntity(Comment existingComment, CommentUpdateRequest commentUpdateRequest) { + existingComment.setContent(commentUpdateRequest.getContent()); + } + + private void deleteComment(Comment comment) { + comment.setDeleteStatus(DeleteStatus.T); + } + +} diff --git a/src/main/java/org/ai/roboadvisor/domain/community/service/PostService.java b/src/main/java/org/ai/roboadvisor/domain/community/service/PostService.java index 5ccb6f9..cdd628b 100644 --- a/src/main/java/org/ai/roboadvisor/domain/community/service/PostService.java +++ b/src/main/java/org/ai/roboadvisor/domain/community/service/PostService.java @@ -2,9 +2,12 @@ import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; +import org.ai.roboadvisor.domain.community.dto.request.PostDeleteRequest; import org.ai.roboadvisor.domain.community.dto.request.PostRequest; import org.ai.roboadvisor.domain.community.dto.response.PostResponse; +import org.ai.roboadvisor.domain.community.entity.DeleteStatus; import org.ai.roboadvisor.domain.community.entity.Post; +import org.ai.roboadvisor.domain.community.repository.CommentRepository; import org.ai.roboadvisor.domain.community.repository.PostRepository; import org.ai.roboadvisor.domain.tendency.entity.Tendency; import org.ai.roboadvisor.global.exception.CustomException; @@ -12,14 +15,13 @@ import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; -import static org.ai.roboadvisor.global.exception.ErrorIntValue.*; - @Slf4j @RequiredArgsConstructor @Service public class PostService { private final PostRepository postRepository; + private final CommentRepository commentRepository; @Transactional public PostResponse save(PostRequest postRequest) { @@ -32,8 +34,20 @@ public PostResponse save(PostRequest postRequest) { // save savePost(newPost); - return PostResponse.of(newPost.getId(), newPost.getTendency(), newPost.getNickname(), - newPost.getContent(), newPost.getCreatedDateTime(), newPost.getViewCount()); + return PostResponse.fromPostEntity(newPost); + } + + @Transactional + public PostResponse getPostById(Long id) { + Post post = postRepository.findPostById(id).orElseThrow(() -> { + // Throw a more specific exception, e.g., PostNotFoundException + return new CustomException(ErrorCode.POST_ID_NOT_EXISTS); + }); + + // update view + post.setViewCount(post.getViewCount() + 1); + + return PostResponse.fromPostEntity(post); } @Transactional @@ -44,7 +58,7 @@ public PostResponse update(Long postId, PostRequest postRequest) { checkTendencyIsValid(postRequest.getTendency()); // validate if user has authority - validateUserHasAuthority(postRequest, existingPost); + validateUserHasAuthority(postRequest.getNickname(), existingPost); // update updatePostEntity(existingPost, postRequest); @@ -53,19 +67,19 @@ public PostResponse update(Long postId, PostRequest postRequest) { } @Transactional - public int delete(Long postId, PostRequest postRequest) { + public PostResponse delete(Long postId, PostDeleteRequest postDeleteRequest) { Post existingPost = findExistingPostById(postId); // validate if user has authority - validateUserHasAuthority(postRequest, existingPost); + validateUserHasAuthority(postDeleteRequest.getNickname(), existingPost); - try { - postRepository.delete(existingPost); - return SUCCESS.getValue(); - } catch (RuntimeException e) { - log.error("e : ", e); - return INTERNAL_SERVER_ERROR.getValue(); - } + // manually delete a post + deletePost(existingPost); + + // manually delete comments related to a post + commentRepository.markCommentsAsDeleted(postId, DeleteStatus.T); + + return PostResponse.fromPostEntity(existingPost); } private void checkTendencyIsValid(Tendency tendency) { @@ -88,8 +102,8 @@ private Post findExistingPostById(Long postId) { .orElseThrow(() -> new CustomException(ErrorCode.INTERNAL_SERVER_ERROR)); } - private void validateUserHasAuthority(PostRequest postRequest, Post existingPost) { - if (!postRequest.getNickname().equals(existingPost.getNickname())) { + private void validateUserHasAuthority(String requestNickname, Post existingPost) { + if (!(requestNickname.equals(existingPost.getNickname()))) { throw new CustomException(ErrorCode.USER_HAS_NOT_AUTHORIZED); } } @@ -98,4 +112,8 @@ private void updatePostEntity(Post existingPost, PostRequest postRequest) { existingPost.setTendency(postRequest.getTendency()); existingPost.setContent(postRequest.getContent()); } + + private void deletePost(Post post) { + post.setDeleteStatus(DeleteStatus.T); + } } diff --git a/src/main/java/org/ai/roboadvisor/domain/community/swagger_annotation/board/getAllPostsByType/getAllPostsByType_BAD_REQUEST.java b/src/main/java/org/ai/roboadvisor/domain/community/swagger_annotation/board/getAllPostsByType/getAllPostsByType_BAD_REQUEST.java new file mode 100644 index 0000000..77e95aa --- /dev/null +++ b/src/main/java/org/ai/roboadvisor/domain/community/swagger_annotation/board/getAllPostsByType/getAllPostsByType_BAD_REQUEST.java @@ -0,0 +1,31 @@ +package org.ai.roboadvisor.domain.community.swagger_annotation.board.getAllPostsByType; + +import io.swagger.v3.oas.annotations.media.Content; +import io.swagger.v3.oas.annotations.media.ExampleObject; +import io.swagger.v3.oas.annotations.media.Schema; +import io.swagger.v3.oas.annotations.responses.ApiResponse; +import org.ai.roboadvisor.global.common.dto.SuccessApiResponse; + +import java.lang.annotation.*; + +@Target({ElementType.METHOD}) +@Retention(RetentionPolicy.RUNTIME) +@Inherited +@ApiResponse(responseCode = "400", description = "게시글 번호가 잘못 입력된 경우, 혹은 존재하지 않는 경우", + content = @Content(schema = @Schema(implementation = SuccessApiResponse.class), + examples = + @ExampleObject(name = "example", + description = "게시글 번호가 잘못 입력된 경우, 혹은 존재하지 않는 경우 예시", + value = """ + { + "code": 400, + "message": "요청하신 게시글 id가 존재하지 않습니다.", + "data": null + } + """ + ) + + )) +public @interface getAllPostsByType_BAD_REQUEST { +} + diff --git a/src/main/java/org/ai/roboadvisor/domain/community/swagger_annotation/board/getAllPostsByType/getAllPostsByType_OK.java b/src/main/java/org/ai/roboadvisor/domain/community/swagger_annotation/board/getAllPostsByType/getAllPostsByType_OK.java new file mode 100644 index 0000000..b2c8385 --- /dev/null +++ b/src/main/java/org/ai/roboadvisor/domain/community/swagger_annotation/board/getAllPostsByType/getAllPostsByType_OK.java @@ -0,0 +1,118 @@ +package org.ai.roboadvisor.domain.community.swagger_annotation.board.getAllPostsByType; + +import io.swagger.v3.oas.annotations.media.Content; +import io.swagger.v3.oas.annotations.media.ExampleObject; +import io.swagger.v3.oas.annotations.media.Schema; +import io.swagger.v3.oas.annotations.responses.ApiResponse; +import org.ai.roboadvisor.global.common.dto.SuccessApiResponse; + +import java.lang.annotation.*; + +@Target({ElementType.METHOD}) +@Retention(RetentionPolicy.RUNTIME) +@Inherited +@ApiResponse(responseCode = "200", description = """ + 게시글 전체 불러오기 + + Pagination을 사용하여, page=0, 1, 2, ... 숫자를 증가시킴에 따라 연속해서 게시글을 가져올 수 있다. + + 하나의 page 마다 10개의 게시글을 가져오도록 구현되어 있으며, 이는 변경이 가능하다. + + 요청 예시는 다음과 같다: [api url]/api/community/board?tendency=LION&page=0 + + data Array 내에서 각 인덱스가 하나의 게시글 정보이다. + + id: 게시글 id, tendency: 투자 성향, nickname: 작성자 닉네임, content: 게시글 내용, + + createdDateTime: 게시글 작성 시간, viewCount: 조회수, commentCount: 게시글 내의 댓글 개수 + """, + content = @Content(schema = @Schema(implementation = SuccessApiResponse.class), + examples = + @ExampleObject(name = "example", + description = "정상 응답", + value = """ + { + "code": 200, + "message": "게시글을 불러오는데 성공하셨습니다", + "data": [ + { + "id": 1, + "tendency": "LION", + "nickname": "testUser", + "content": "안녕하세요3333", + "createdDateTime": "2023-09-21 01:06:20", + "viewCount": 9, + "commentCount": 2 + }, + { + "id": 3, + "tendency": "LION", + "nickname": "testUser", + "content": "안녕하세요", + "createdDateTime": "2023-09-23 14:44:14", + "viewCount": 0, + "commentCount": 0 + }, + { + "id": 4, + "tendency": "LION", + "nickname": "testUser", + "content": "안녕하세요", + "createdDateTime": "2023-09-23 14:44:15", + "viewCount": 0, + "commentCount": 0 + }, + { + "id": 5, + "tendency": "LION", + "nickname": "testUser", + "content": "안녕하세요", + "createdDateTime": "2023-09-23 14:44:15", + "viewCount": 0, + "commentCount": 0 + }, + { + "id": 6, + "tendency": "LION", + "nickname": "testUser", + "content": "안녕하세요", + "createdDateTime": "2023-09-23 14:44:16", + "viewCount": 0, + "commentCount": 0 + }, + { + "id": 7, + "tendency": "LION", + "nickname": "testUser", + "content": "안녕하세요", + "createdDateTime": "2023-09-23 14:44:16", + "viewCount": 0, + "commentCount": 0 + }, + { + "id": 8, + "tendency": "LION", + "nickname": "testUser", + "content": "안녕하세요", + "createdDateTime": "2023-09-23 14:44:17", + "viewCount": 0, + "commentCount": 0 + }, + { + "id": 9, + "tendency": "LION", + "nickname": "testUser", + "content": "안녕하세요", + "createdDateTime": "2023-09-23 14:44:18", + "viewCount": 0, + "commentCount": 0 + } + ] + } + """ + ) + + )) +public @interface getAllPostsByType_OK { +} + diff --git a/src/main/java/org/ai/roboadvisor/domain/community/swagger_annotation/comment/delete/delete_BAD_REQUEST.java b/src/main/java/org/ai/roboadvisor/domain/community/swagger_annotation/comment/delete/delete_BAD_REQUEST.java new file mode 100644 index 0000000..7e67dc9 --- /dev/null +++ b/src/main/java/org/ai/roboadvisor/domain/community/swagger_annotation/comment/delete/delete_BAD_REQUEST.java @@ -0,0 +1,30 @@ +package org.ai.roboadvisor.domain.community.swagger_annotation.comment.delete; + +import io.swagger.v3.oas.annotations.media.Content; +import io.swagger.v3.oas.annotations.media.ExampleObject; +import io.swagger.v3.oas.annotations.media.Schema; +import io.swagger.v3.oas.annotations.responses.ApiResponse; +import org.ai.roboadvisor.global.common.dto.SuccessApiResponse; + +import java.lang.annotation.*; + +@Target({ElementType.METHOD}) +@Retention(RetentionPolicy.RUNTIME) +@Inherited +@ApiResponse(responseCode = "400", description = "게시글 번호가 잘못 입력된 경우, 혹은 존재하지 않는 경우", + content = @Content(schema = @Schema(implementation = SuccessApiResponse.class), + examples = + @ExampleObject(name = "example", + description = "게시글 번호가 잘못 입력된 경우, 혹은 존재하지 않는 경우 예시", + value = """ + { + "code": 400, + "message": "요청하신 게시글 id가 존재하지 않습니다.", + "data": null + } + """ + ) + )) +public @interface delete_BAD_REQUEST { +} + diff --git a/src/main/java/org/ai/roboadvisor/domain/community/swagger_annotation/comment/delete/delete_OK.java b/src/main/java/org/ai/roboadvisor/domain/community/swagger_annotation/comment/delete/delete_OK.java new file mode 100644 index 0000000..3501ce9 --- /dev/null +++ b/src/main/java/org/ai/roboadvisor/domain/community/swagger_annotation/comment/delete/delete_OK.java @@ -0,0 +1,36 @@ +package org.ai.roboadvisor.domain.community.swagger_annotation.comment.delete; + +import io.swagger.v3.oas.annotations.media.Content; +import io.swagger.v3.oas.annotations.media.ExampleObject; +import io.swagger.v3.oas.annotations.media.Schema; +import io.swagger.v3.oas.annotations.responses.ApiResponse; +import org.ai.roboadvisor.global.common.dto.SuccessApiResponse; + +import java.lang.annotation.*; + +@Target({ElementType.METHOD}) +@Retention(RetentionPolicy.RUNTIME) +@Inherited +@ApiResponse(responseCode = "200", description = """ + 댓글이 정상적으로 삭제된 경우: data 내에 응답 객체는 '댓글 작성', '댓글 수정' 과 동일하다. + """, + content = @Content(schema = @Schema(implementation = SuccessApiResponse.class), + examples = @ExampleObject(name = "example", + description = "정상 응답 예시", + value = """ + { + "code": 200, + "message": "댓글 삭제가 정상적으로 처리되었습니다", + "data": { + "id": 1, + "postId": 1, + "nickname": "testUser", + "content": "안녕하세요 댓글 1", + "createdDateTime": "2023-09-25 04:28:41" + } + } + """ + ))) +public @interface delete_OK { +} + diff --git a/src/main/java/org/ai/roboadvisor/domain/community/swagger_annotation/comment/delete/delete_UNAUTHORIZED.java b/src/main/java/org/ai/roboadvisor/domain/community/swagger_annotation/comment/delete/delete_UNAUTHORIZED.java new file mode 100644 index 0000000..6effdce --- /dev/null +++ b/src/main/java/org/ai/roboadvisor/domain/community/swagger_annotation/comment/delete/delete_UNAUTHORIZED.java @@ -0,0 +1,29 @@ +package org.ai.roboadvisor.domain.community.swagger_annotation.comment.delete; + +import io.swagger.v3.oas.annotations.media.Content; +import io.swagger.v3.oas.annotations.media.ExampleObject; +import io.swagger.v3.oas.annotations.media.Schema; +import io.swagger.v3.oas.annotations.responses.ApiResponse; +import org.ai.roboadvisor.global.common.dto.SuccessApiResponse; + +import java.lang.annotation.*; + +@Target({ElementType.METHOD}) +@Retention(RetentionPolicy.RUNTIME) +@Inherited +@ApiResponse(responseCode = "401", description = "게시글과 댓글의 투자 성향이 다른 경우, 즉 댓글 작성 권한이 없는 경우", + content = @Content(schema = @Schema(implementation = SuccessApiResponse.class), + examples = @ExampleObject(name = "example", + description = "게시글과 댓글의 투자 성향이 다른 경우 예시", + value = """ + { + "code": 401, + "message": "사용자에게 권한이 존재하지 않습니다", + "data": null + } + """ + ) + )) +public @interface delete_UNAUTHORIZED { +} + diff --git a/src/main/java/org/ai/roboadvisor/domain/community/swagger_annotation/comment/save/save_BAD_REQUEST.java b/src/main/java/org/ai/roboadvisor/domain/community/swagger_annotation/comment/save/save_BAD_REQUEST.java new file mode 100644 index 0000000..605edd5 --- /dev/null +++ b/src/main/java/org/ai/roboadvisor/domain/community/swagger_annotation/comment/save/save_BAD_REQUEST.java @@ -0,0 +1,31 @@ +package org.ai.roboadvisor.domain.community.swagger_annotation.comment.save; + +import io.swagger.v3.oas.annotations.media.Content; +import io.swagger.v3.oas.annotations.media.ExampleObject; +import io.swagger.v3.oas.annotations.media.Schema; +import io.swagger.v3.oas.annotations.responses.ApiResponse; +import org.ai.roboadvisor.global.common.dto.SuccessApiResponse; + +import java.lang.annotation.*; + +@Target({ElementType.METHOD}) +@Retention(RetentionPolicy.RUNTIME) +@Inherited +@ApiResponse(responseCode = "400", description = "게시글 번호가 잘못 입력된 경우, 혹은 존재하지 않는 경우", + content = @Content(schema = @Schema(implementation = SuccessApiResponse.class), + examples = + @ExampleObject(name = "example", + description = "게시글 번호가 잘못 입력된 경우, 혹은 존재하지 않는 경우 예시", + value = """ + { + "code": 400, + "message": "요청하신 게시글 id가 존재하지 않습니다.", + "data": null + } + """ + ) + + )) +public @interface save_BAD_REQUEST { +} + diff --git a/src/main/java/org/ai/roboadvisor/domain/community/swagger_annotation/comment/save/save_CREATED.java b/src/main/java/org/ai/roboadvisor/domain/community/swagger_annotation/comment/save/save_CREATED.java new file mode 100644 index 0000000..266c777 --- /dev/null +++ b/src/main/java/org/ai/roboadvisor/domain/community/swagger_annotation/comment/save/save_CREATED.java @@ -0,0 +1,40 @@ +package org.ai.roboadvisor.domain.community.swagger_annotation.comment.save; + +import io.swagger.v3.oas.annotations.media.Content; +import io.swagger.v3.oas.annotations.media.ExampleObject; +import io.swagger.v3.oas.annotations.media.Schema; +import io.swagger.v3.oas.annotations.responses.ApiResponse; +import org.ai.roboadvisor.global.common.dto.SuccessApiResponse; + +import java.lang.annotation.*; + +@Target({ElementType.METHOD}) +@Retention(RetentionPolicy.RUNTIME) +@Inherited +@ApiResponse(responseCode = "201", description = """ + 정상 응답. data로 댓글 정보를 리턴한다. + + id: 댓글 고유 번호(식별 번호), postId: 댓글이 작성된 게시글의 번호, nickname: 댓글 작성자 닉네임, + + content: 댓글 작성 내용, createdDateTime: 댓글이 작성된 시간 + """, + content = @Content(schema = @Schema(implementation = SuccessApiResponse.class), + examples = @ExampleObject(name = "example", + description = "정상 응답 예시", + value = """ + { + "code": 201, + "message": "댓글이 정상적으로 등록되었습니다", + "data": { + "id": 2, + "postId": 1, + "nickname": "testUser", + "content": "안녕하세요 댓글 1", + "createdDateTime": "2023-09-25 03:14:21" + } + } + """ + ))) +public @interface save_CREATED { +} + diff --git a/src/main/java/org/ai/roboadvisor/domain/community/swagger_annotation/comment/save/save_UNAUTHORIZED.java b/src/main/java/org/ai/roboadvisor/domain/community/swagger_annotation/comment/save/save_UNAUTHORIZED.java new file mode 100644 index 0000000..085739f --- /dev/null +++ b/src/main/java/org/ai/roboadvisor/domain/community/swagger_annotation/comment/save/save_UNAUTHORIZED.java @@ -0,0 +1,29 @@ +package org.ai.roboadvisor.domain.community.swagger_annotation.comment.save; + +import io.swagger.v3.oas.annotations.media.Content; +import io.swagger.v3.oas.annotations.media.ExampleObject; +import io.swagger.v3.oas.annotations.media.Schema; +import io.swagger.v3.oas.annotations.responses.ApiResponse; +import org.ai.roboadvisor.global.common.dto.SuccessApiResponse; + +import java.lang.annotation.*; + +@Target({ElementType.METHOD}) +@Retention(RetentionPolicy.RUNTIME) +@Inherited +@ApiResponse(responseCode = "401", description = "게시글과 댓글의 투자 성향이 다른 경우, 즉 댓글 작성 권한이 없는 경우", + content = @Content(schema = @Schema(implementation = SuccessApiResponse.class), + examples = @ExampleObject(name = "example", + description = "게시글과 댓글의 투자 성향이 다른 경우 예시", + value = """ + { + "code": 401, + "message": "게시글 수정 혹은 삭제 권한이 존재하지 않습니다", + "data": null + } + """ + ) + )) +public @interface save_UNAUTHORIZED { +} + diff --git a/src/main/java/org/ai/roboadvisor/domain/community/swagger_annotation/comment/update/update_BAD_REQUEST.java b/src/main/java/org/ai/roboadvisor/domain/community/swagger_annotation/comment/update/update_BAD_REQUEST.java new file mode 100644 index 0000000..960e242 --- /dev/null +++ b/src/main/java/org/ai/roboadvisor/domain/community/swagger_annotation/comment/update/update_BAD_REQUEST.java @@ -0,0 +1,30 @@ +package org.ai.roboadvisor.domain.community.swagger_annotation.comment.update; + +import io.swagger.v3.oas.annotations.media.Content; +import io.swagger.v3.oas.annotations.media.ExampleObject; +import io.swagger.v3.oas.annotations.media.Schema; +import io.swagger.v3.oas.annotations.responses.ApiResponse; +import org.ai.roboadvisor.global.common.dto.SuccessApiResponse; + +import java.lang.annotation.*; + +@Target({ElementType.METHOD}) +@Retention(RetentionPolicy.RUNTIME) +@Inherited +@ApiResponse(responseCode = "400", description = "게시글 번호가 잘못 입력된 경우, 혹은 존재하지 않는 경우", + content = @Content(schema = @Schema(implementation = SuccessApiResponse.class), + examples = + @ExampleObject(name = "example", + description = "게시글 번호가 잘못 입력된 경우, 혹은 존재하지 않는 경우 예시", + value = """ + { + "code": 400, + "message": "요청하신 게시글 id가 존재하지 않습니다.", + "data": null + } + """ + ) + )) +public @interface update_BAD_REQUEST { +} + diff --git a/src/main/java/org/ai/roboadvisor/domain/community/swagger_annotation/comment/update/update_OK.java b/src/main/java/org/ai/roboadvisor/domain/community/swagger_annotation/comment/update/update_OK.java new file mode 100644 index 0000000..fff6ce5 --- /dev/null +++ b/src/main/java/org/ai/roboadvisor/domain/community/swagger_annotation/comment/update/update_OK.java @@ -0,0 +1,36 @@ +package org.ai.roboadvisor.domain.community.swagger_annotation.comment.update; + +import io.swagger.v3.oas.annotations.media.Content; +import io.swagger.v3.oas.annotations.media.ExampleObject; +import io.swagger.v3.oas.annotations.media.Schema; +import io.swagger.v3.oas.annotations.responses.ApiResponse; +import org.ai.roboadvisor.global.common.dto.SuccessApiResponse; + +import java.lang.annotation.*; + +@Target({ElementType.METHOD}) +@Retention(RetentionPolicy.RUNTIME) +@Inherited +@ApiResponse(responseCode = "200", description = """ + 댓글이 정상적으로 수정된 경우: data 내에 응답 객체는 '댓글 작성' 과 동일하다. + """, + content = @Content(schema = @Schema(implementation = SuccessApiResponse.class), + examples = @ExampleObject(name = "example", + description = "정상 응답 예시", + value = """ + { + "code": 200, + "message": "댓글 수정이 정상적으로 처리되었습니다", + "data": { + "id": 1, + "postId": 1, + "nickname": "testUser", + "content": "안녕하세요 댓글 13333", + "createdDateTime": "2023-09-25 03:12:52" + } + } + """ + ))) +public @interface update_OK { +} + diff --git a/src/main/java/org/ai/roboadvisor/domain/community/swagger_annotation/comment/update/update_UNAUTHORIZED.java b/src/main/java/org/ai/roboadvisor/domain/community/swagger_annotation/comment/update/update_UNAUTHORIZED.java new file mode 100644 index 0000000..de31eee --- /dev/null +++ b/src/main/java/org/ai/roboadvisor/domain/community/swagger_annotation/comment/update/update_UNAUTHORIZED.java @@ -0,0 +1,29 @@ +package org.ai.roboadvisor.domain.community.swagger_annotation.comment.update; + +import io.swagger.v3.oas.annotations.media.Content; +import io.swagger.v3.oas.annotations.media.ExampleObject; +import io.swagger.v3.oas.annotations.media.Schema; +import io.swagger.v3.oas.annotations.responses.ApiResponse; +import org.ai.roboadvisor.global.common.dto.SuccessApiResponse; + +import java.lang.annotation.*; + +@Target({ElementType.METHOD}) +@Retention(RetentionPolicy.RUNTIME) +@Inherited +@ApiResponse(responseCode = "401", description = "게시글과 댓글의 투자 성향이 다른 경우, 즉 댓글 작성 권한이 없는 경우", + content = @Content(schema = @Schema(implementation = SuccessApiResponse.class), + examples = @ExampleObject(name = "example", + description = "게시글과 댓글의 투자 성향이 다른 경우 예시", + value = """ + { + "code": 401, + "message": "사용자에게 권한이 존재하지 않습니다", + "data": null + } + """ + ) + )) +public @interface update_UNAUTHORIZED { +} + diff --git a/src/main/java/org/ai/roboadvisor/domain/community/swagger_annotation/post/delete/delete_OK.java b/src/main/java/org/ai/roboadvisor/domain/community/swagger_annotation/post/delete/delete_OK.java new file mode 100644 index 0000000..ba4bc20 --- /dev/null +++ b/src/main/java/org/ai/roboadvisor/domain/community/swagger_annotation/post/delete/delete_OK.java @@ -0,0 +1,37 @@ +package org.ai.roboadvisor.domain.community.swagger_annotation.post.delete; + +import io.swagger.v3.oas.annotations.media.Content; +import io.swagger.v3.oas.annotations.media.ExampleObject; +import io.swagger.v3.oas.annotations.media.Schema; +import io.swagger.v3.oas.annotations.responses.ApiResponse; +import org.ai.roboadvisor.global.common.dto.SuccessApiResponse; + +import java.lang.annotation.*; + +@Target({ElementType.METHOD}) +@Retention(RetentionPolicy.RUNTIME) +@Inherited +@ApiResponse(responseCode = "200", description = """ + 게시글이 정상적으로 삭제된 경우: 응답 객체는 '게시글 작성', '게시글 수정' 과 동일하다. + """, + content = @Content(schema = @Schema(implementation = SuccessApiResponse.class), + examples = @ExampleObject(name = "example", + description = "정상 응답 예시", + value = """ + { + "code": 200, + "message": "게시글 삭제가 정상적으로 처리되었습니다", + "data": { + "id": 12, + "tendency": "LION", + "nickname": "testUser", + "content": "안녕하세요", + "createdDateTime": "2023-09-25 04:47:33", + "viewCount": 0 + } + } + """ + ))) +public @interface delete_OK { +} + diff --git a/src/main/java/org/ai/roboadvisor/domain/community/swagger_annotation/post/delete/delete_UNAUTHORIZED.java b/src/main/java/org/ai/roboadvisor/domain/community/swagger_annotation/post/delete/delete_UNAUTHORIZED.java new file mode 100644 index 0000000..36a8011 --- /dev/null +++ b/src/main/java/org/ai/roboadvisor/domain/community/swagger_annotation/post/delete/delete_UNAUTHORIZED.java @@ -0,0 +1,28 @@ +package org.ai.roboadvisor.domain.community.swagger_annotation.post.delete; + +import io.swagger.v3.oas.annotations.media.Content; +import io.swagger.v3.oas.annotations.media.ExampleObject; +import io.swagger.v3.oas.annotations.media.Schema; +import io.swagger.v3.oas.annotations.responses.ApiResponse; +import org.ai.roboadvisor.global.common.dto.SuccessApiResponse; + +import java.lang.annotation.*; + +@Target({ElementType.METHOD}) +@Retention(RetentionPolicy.RUNTIME) +@Inherited +@ApiResponse(responseCode = "401", description = "게시글 삭제 권한이 없는 경우", + content = @Content(schema = @Schema(implementation = SuccessApiResponse.class), + examples = @ExampleObject(name = "example", + description = "게시글 삭제 권한이 없는 예시", + value = """ + { + "code": 401, + "message": "게시글 수정 혹은 삭제 권한이 존재하지 않습니다", + "data": null + } + """ + ))) +public @interface delete_UNAUTHORIZED { +} + diff --git a/src/main/java/org/ai/roboadvisor/domain/community/swagger_annotation/post/getPostById/getPostById_BAD_REQUEST.java b/src/main/java/org/ai/roboadvisor/domain/community/swagger_annotation/post/getPostById/getPostById_BAD_REQUEST.java new file mode 100644 index 0000000..a7f81fe --- /dev/null +++ b/src/main/java/org/ai/roboadvisor/domain/community/swagger_annotation/post/getPostById/getPostById_BAD_REQUEST.java @@ -0,0 +1,31 @@ +package org.ai.roboadvisor.domain.community.swagger_annotation.post.getPostById; + +import io.swagger.v3.oas.annotations.media.Content; +import io.swagger.v3.oas.annotations.media.ExampleObject; +import io.swagger.v3.oas.annotations.media.Schema; +import io.swagger.v3.oas.annotations.responses.ApiResponse; +import org.ai.roboadvisor.global.common.dto.SuccessApiResponse; + +import java.lang.annotation.*; + +@Target({ElementType.METHOD}) +@Retention(RetentionPolicy.RUNTIME) +@Inherited +@ApiResponse(responseCode = "400", description = "게시글 번호가 잘못 입력된 경우, 혹은 존재하지 않는 경우", + content = @Content(schema = @Schema(implementation = SuccessApiResponse.class), + examples = + @ExampleObject(name = "example", + description = "게시글 번호가 잘못 입력된 경우, 혹은 존재하지 않는 경우 예시", + value = """ + { + "code": 400, + "message": "요청하신 게시글 id가 존재하지 않습니다.", + "data": null + } + """ + ) + + )) +public @interface getPostById_BAD_REQUEST { +} + diff --git a/src/main/java/org/ai/roboadvisor/domain/community/swagger_annotation/post/getPostById/getPostById_OK.java b/src/main/java/org/ai/roboadvisor/domain/community/swagger_annotation/post/getPostById/getPostById_OK.java new file mode 100644 index 0000000..729ba2b --- /dev/null +++ b/src/main/java/org/ai/roboadvisor/domain/community/swagger_annotation/post/getPostById/getPostById_OK.java @@ -0,0 +1,47 @@ +package org.ai.roboadvisor.domain.community.swagger_annotation.post.getPostById; + +import io.swagger.v3.oas.annotations.media.Content; +import io.swagger.v3.oas.annotations.media.ExampleObject; +import io.swagger.v3.oas.annotations.media.Schema; +import io.swagger.v3.oas.annotations.responses.ApiResponse; +import org.ai.roboadvisor.global.common.dto.SuccessApiResponse; + +import java.lang.annotation.*; + +@Target({ElementType.METHOD}) +@Retention(RetentionPolicy.RUNTIME) +@Inherited +@ApiResponse(responseCode = "200", description = """ + 특정 게시글을 조회하는 경우 + + comments Array 객체의 인덱스마다 댓글 객체의 정보가 담긴다. + """, + content = @Content(schema = @Schema(implementation = SuccessApiResponse.class), + examples = @ExampleObject(name = "example", + description = "정상 응답 예시", + value = """ + { + "code": 200, + "message": "게시글을 조회하는데 성공하셨습니다", + "data": { + "id": 1, + "tendency": "LION", + "nickname": "testUser", + "content": "안녕하세요3333", + "createdDateTime": "2023-09-21 01:06:20", + "viewCount": 22, + "comments": [ + { + "id": 2, + "nickname": "testUser", + "content": "안녕하세요 댓글 1", + "createdDateTime": "2023-09-25 04:28:55" + } + ] + } + } + """ + ))) +public @interface getPostById_OK { +} + diff --git a/src/main/java/org/ai/roboadvisor/domain/community/swagger_annotation/post/save/save_BAD_REQUEST.java b/src/main/java/org/ai/roboadvisor/domain/community/swagger_annotation/post/save/save_BAD_REQUEST.java new file mode 100644 index 0000000..c7b9019 --- /dev/null +++ b/src/main/java/org/ai/roboadvisor/domain/community/swagger_annotation/post/save/save_BAD_REQUEST.java @@ -0,0 +1,28 @@ +package org.ai.roboadvisor.domain.community.swagger_annotation.post.save; + +import io.swagger.v3.oas.annotations.media.Content; +import io.swagger.v3.oas.annotations.media.ExampleObject; +import io.swagger.v3.oas.annotations.media.Schema; +import io.swagger.v3.oas.annotations.responses.ApiResponse; +import org.ai.roboadvisor.global.common.dto.SuccessApiResponse; + +import java.lang.annotation.*; + +@Target({ElementType.METHOD}) +@Retention(RetentionPolicy.RUNTIME) +@Inherited +@ApiResponse(responseCode = "400", description = "투자 성향이 잘못 입력된 경우", + content = @Content(schema = @Schema(implementation = SuccessApiResponse.class), + examples = @ExampleObject(name = "example", + description = "투자 성향이 잘못 입력된 경우 예시", + value = """ + { + "code": 400, + "message": "잘못된 투자 성향 형식이 입력되었습니다", + "data": null + } + """ + ))) +public @interface save_BAD_REQUEST { +} + diff --git a/src/main/java/org/ai/roboadvisor/domain/community/swagger_annotation/post/save/save_CREATED.java b/src/main/java/org/ai/roboadvisor/domain/community/swagger_annotation/post/save/save_CREATED.java new file mode 100644 index 0000000..1d9f3c5 --- /dev/null +++ b/src/main/java/org/ai/roboadvisor/domain/community/swagger_annotation/post/save/save_CREATED.java @@ -0,0 +1,41 @@ +package org.ai.roboadvisor.domain.community.swagger_annotation.post.save; + +import io.swagger.v3.oas.annotations.media.Content; +import io.swagger.v3.oas.annotations.media.ExampleObject; +import io.swagger.v3.oas.annotations.media.Schema; +import io.swagger.v3.oas.annotations.responses.ApiResponse; +import org.ai.roboadvisor.global.common.dto.SuccessApiResponse; + +import java.lang.annotation.*; + +@Target({ElementType.METHOD}) +@Retention(RetentionPolicy.RUNTIME) +@Inherited +@ApiResponse(responseCode = "201", description = """ + 정상 응답. data로 게시글 정보를 리턴한다. + + id: 게시글 고유 번호(식별 번호), tendency: 투자 성향, nickname: 게시글 작성자 닉네임, + + content: 게시글 작성 내용, time: 게시글 작성 시간, viewcount: 조회수 + """, + content = @Content(schema = @Schema(implementation = SuccessApiResponse.class), + examples = @ExampleObject(name = "example", + description = "정상 응답 예시", + value = """ + { + "code": 201, + "message": "게시글이 정상적으로 등록되었습니다", + "data": { + "id": 12, + "tendency": "LION", + "nickname": "testUser", + "content": "안녕하세요", + "createdDateTime": "2023-09-25 04:47:32", + "viewCount": 0 + } + } + """ + ))) +public @interface save_CREATED { +} + diff --git a/src/main/java/org/ai/roboadvisor/domain/community/swagger_annotation/post/update/update_BAD_REQUEST.java b/src/main/java/org/ai/roboadvisor/domain/community/swagger_annotation/post/update/update_BAD_REQUEST.java new file mode 100644 index 0000000..ce6025e --- /dev/null +++ b/src/main/java/org/ai/roboadvisor/domain/community/swagger_annotation/post/update/update_BAD_REQUEST.java @@ -0,0 +1,28 @@ +package org.ai.roboadvisor.domain.community.swagger_annotation.post.update; + +import io.swagger.v3.oas.annotations.media.Content; +import io.swagger.v3.oas.annotations.media.ExampleObject; +import io.swagger.v3.oas.annotations.media.Schema; +import io.swagger.v3.oas.annotations.responses.ApiResponse; +import org.ai.roboadvisor.global.common.dto.SuccessApiResponse; + +import java.lang.annotation.*; + +@Target({ElementType.METHOD}) +@Retention(RetentionPolicy.RUNTIME) +@Inherited +@ApiResponse(responseCode = "400", description = "투자 성향이 잘못 입력된 경우", + content = @Content(schema = @Schema(implementation = SuccessApiResponse.class), + examples = @ExampleObject(name = "example", + description = "투자 성향이 잘못 입력된 경우 예시", + value = """ + { + "code": 400, + "message": "잘못된 투자 성향 형식이 입력되었습니다", + "data": null + } + """ + ))) +public @interface update_BAD_REQUEST { +} + diff --git a/src/main/java/org/ai/roboadvisor/domain/community/swagger_annotation/post/update/update_OK.java b/src/main/java/org/ai/roboadvisor/domain/community/swagger_annotation/post/update/update_OK.java new file mode 100644 index 0000000..08f363d --- /dev/null +++ b/src/main/java/org/ai/roboadvisor/domain/community/swagger_annotation/post/update/update_OK.java @@ -0,0 +1,37 @@ +package org.ai.roboadvisor.domain.community.swagger_annotation.post.update; + +import io.swagger.v3.oas.annotations.media.Content; +import io.swagger.v3.oas.annotations.media.ExampleObject; +import io.swagger.v3.oas.annotations.media.Schema; +import io.swagger.v3.oas.annotations.responses.ApiResponse; +import org.ai.roboadvisor.global.common.dto.SuccessApiResponse; + +import java.lang.annotation.*; + +@Target({ElementType.METHOD}) +@Retention(RetentionPolicy.RUNTIME) +@Inherited +@ApiResponse(responseCode = "200", description = """ + 게시글이 정상적으로 수정된 경우: 응답 객체는 '게시글 작성' 과 동일하다. + """, + content = @Content(schema = @Schema(implementation = SuccessApiResponse.class), + examples = @ExampleObject(name = "example", + description = "정상 응답 예시", + value = """ + { + "code": 200, + "message": "게시글 수정이 정상적으로 처리되었습니다", + "data": { + "id": 1, + "tendency": "LION", + "nickname": "testUser", + "content": "안녕하세요3333", + "createdDateTime": "2023-09-21 01:06:20", + "viewCount": 0 + } + } + """ + ))) +public @interface update_OK { +} + diff --git a/src/main/java/org/ai/roboadvisor/domain/community/swagger_annotation/post/update/update_UNAUTHORIZED.java b/src/main/java/org/ai/roboadvisor/domain/community/swagger_annotation/post/update/update_UNAUTHORIZED.java new file mode 100644 index 0000000..f7d26d3 --- /dev/null +++ b/src/main/java/org/ai/roboadvisor/domain/community/swagger_annotation/post/update/update_UNAUTHORIZED.java @@ -0,0 +1,35 @@ +package org.ai.roboadvisor.domain.community.swagger_annotation.post.update; + +import io.swagger.v3.oas.annotations.media.Content; +import io.swagger.v3.oas.annotations.media.ExampleObject; +import io.swagger.v3.oas.annotations.media.Schema; +import io.swagger.v3.oas.annotations.responses.ApiResponse; +import org.ai.roboadvisor.global.common.dto.SuccessApiResponse; + +import java.lang.annotation.*; + +@Target({ElementType.METHOD}) +@Retention(RetentionPolicy.RUNTIME) +@Inherited +@ApiResponse(responseCode = "401", description = "게시글 수정 권한이 없는 경우", + content = @Content(schema = @Schema(implementation = SuccessApiResponse.class), + examples = @ExampleObject(name = "example", + description = "게시글 수정 권한이 없는 경우 예시", + value = """ + { + "code": 401, + "message": "게시글 수정 혹은 삭제 권한이 존재하지 않습니다", + "data": { + "id": 1, + "tendency": "LION", + "nickname": "testUser", + "content": "안녕하세요3333", + "createdDateTime": "2023-09-21 01:06:20", + "viewCount": 0 + } + } + """ + ))) +public @interface update_UNAUTHORIZED { +} + diff --git a/src/main/java/org/ai/roboadvisor/global/exception/ErrorCode.java b/src/main/java/org/ai/roboadvisor/global/exception/ErrorCode.java index 8665ff1..0cae561 100644 --- a/src/main/java/org/ai/roboadvisor/global/exception/ErrorCode.java +++ b/src/main/java/org/ai/roboadvisor/global/exception/ErrorCode.java @@ -21,7 +21,10 @@ public enum ErrorCode { TIME_INPUT_INVALID(HttpStatus.BAD_REQUEST, "CH01", "time 형식을 yyyy-MM-dd HH:mm:ss으로 작성해 주세요"), // community - USER_HAS_NOT_AUTHORIZED(HttpStatus.UNAUTHORIZED, "CO01", "게시글 수정 혹은 삭제 권한이 존재하지 않습니다"), + POST_ID_NOT_EXISTS(HttpStatus.BAD_REQUEST, "C001", "요청하신 게시글 id가 존재하지 않습니다."), + USER_HAS_NOT_AUTHORIZED(HttpStatus.UNAUTHORIZED, "CO02", "사용자에게 권한이 존재하지 않습니다"), + TENDENCY_NOT_MATCH_BETWEEN_POST_AND_COMMENT(HttpStatus.UNAUTHORIZED, + "CO03", "게시글 수정 혹은 삭제 권한이 존재하지 않습니다"), // tendency TENDENCY_INPUT_INVALID(HttpStatus.BAD_REQUEST, "TE01", "잘못된 투자 성향 형식이 입력되었습니다"); diff --git a/src/main/java/org/ai/roboadvisor/global/exception/SuccessCode.java b/src/main/java/org/ai/roboadvisor/global/exception/SuccessCode.java index 76f5dc3..229463b 100644 --- a/src/main/java/org/ai/roboadvisor/global/exception/SuccessCode.java +++ b/src/main/java/org/ai/roboadvisor/global/exception/SuccessCode.java @@ -24,6 +24,10 @@ public enum SuccessCode { // community POST_UPDATE_SUCCESS(HttpStatus.OK.value(), "게시글 수정이 정상적으로 처리되었습니다"), POST_DELETE_SUCCESS(HttpStatus.OK.value(), "게시글 삭제가 정상적으로 처리되었습니다"), + POST_VIEW_SUCCESS(HttpStatus.OK.value(), "게시글을 조회하는데 성공하셨습니다"), + BOARD_ALL_VIEW_SUCCESS(HttpStatus.OK.value(), "게시글을 불러오는데 성공하셨습니다"), + COMMENT_UPDATE_SUCCESS(HttpStatus.OK.value(), "댓글 수정이 정상적으로 처리되었습니다"), + COMMENT_DELETE_SUCCESS(HttpStatus.OK.value(), "댓글 삭제가 정상적으로 처리되었습니다"), /** * 201 CREATED @@ -38,6 +42,7 @@ public enum SuccessCode { // community POST_CREATED_SUCCESS(HttpStatus.CREATED.value(), "게시글이 정상적으로 등록되었습니다"), + COMMENT_CREATED_SUCCESS(HttpStatus.CREATED.value(), "댓글이 정상적으로 등록되었습니다"), // tendency TENDENCY_UPDATE_SUCCESS(HttpStatus.CREATED.value(), "투자 성향 테스트 결과가 정상적으로 등록되었습니다");