diff --git a/src/main/java/sixgaezzang/sidepeek/comments/controller/CommentController.java b/src/main/java/sixgaezzang/sidepeek/comments/controller/CommentController.java new file mode 100644 index 00000000..7d9363b4 --- /dev/null +++ b/src/main/java/sixgaezzang/sidepeek/comments/controller/CommentController.java @@ -0,0 +1,88 @@ +package sixgaezzang.sidepeek.comments.controller; + +import io.swagger.v3.oas.annotations.Operation; +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 lombok.RequiredArgsConstructor; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.DeleteMapping; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.PutMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; +import sixgaezzang.sidepeek.comments.dto.request.CommentRequest; +import sixgaezzang.sidepeek.comments.dto.response.CommentResponse; +import sixgaezzang.sidepeek.common.annotation.Login; + +@RestController +@RequestMapping("/projects/{projectId}/comments") +@Tag(name = "Project Comment", description = "Project Comment API") +@RequiredArgsConstructor +public class CommentController { + + @PostMapping + @Operation(summary = "댓글 생성") + @ApiResponse(responseCode = "201", description = "댓글 생성 성공") + public ResponseEntity save( + @Schema(description = "로그인한 회원 식별자", example = "1") + @Login + Long loginId, + + @Schema(description = "생성할 댓글의 프로젝트 식별자", example = "1") + @PathVariable + Long projectId, + + @Valid + @RequestBody + CommentRequest request + ) { + return null; + } + + @PutMapping("/{id}") + @Operation(summary = "댓글 수정") + @ApiResponse(responseCode = "200", description = "댓글 수정 성공") + public ResponseEntity update( + @Schema(description = "로그인한 회원 식별자", example = "1") + @Login + Long loginId, + + @Schema(description = "수정할 댓글의 프로젝트 식별자", example = "1") + @PathVariable + Long projectId, + + @Schema(description = "수정할 댓글 식별자", example = "1") + @PathVariable(value = "id") + Long commentId, + + @Valid + @RequestBody + CommentRequest request + ) { + return null; + } + + @DeleteMapping("/{id}") + @Operation(summary = "댓글 삭제") + @ApiResponse(responseCode = "204", description = "댓글 삭제 성공") + public ResponseEntity delete( + @Schema(description = "로그인한 회원 식별자", example = "1") + @Login + Long loginId, + + @Schema(description = "삭제할 댓글의 프로젝트 식별자", example = "1") + @PathVariable + Long projectId, + + @Schema(description = "삭제할 댓글 식별자", example = "1") + @PathVariable(value = "id") + Long commentId + ) { + return null; + } + +} diff --git a/src/main/java/sixgaezzang/sidepeek/comments/dto/request/CommentRequest.java b/src/main/java/sixgaezzang/sidepeek/comments/dto/request/CommentRequest.java new file mode 100644 index 00000000..d89df372 --- /dev/null +++ b/src/main/java/sixgaezzang/sidepeek/comments/dto/request/CommentRequest.java @@ -0,0 +1,30 @@ +package sixgaezzang.sidepeek.comments.dto.request; + +import static sixgaezzang.sidepeek.comments.exception.message.CommentErrorMessage.CONTENT_IS_NULL; +import static sixgaezzang.sidepeek.comments.exception.message.CommentErrorMessage.CONTENT_OVER_MAX_LENGTH; +import static sixgaezzang.sidepeek.comments.exception.message.CommentErrorMessage.IS_ANONYMOUS_IS_NULL; +import static sixgaezzang.sidepeek.comments.util.CommentConstant.MAX_COMMENT_LENGTH; +import static sixgaezzang.sidepeek.common.util.CommonConstant.MIN_ID; + +import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.validation.constraints.Min; +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.NotNull; +import jakarta.validation.constraints.Size; + +@Schema(description = "댓글 생성/수정 요청 정보") +public record CommentRequest( + @Schema(description = "댓글 작성자 식별자", example = "1") + @Min(value = MIN_ID, message = "작성자 id는 " + MIN_ID + "보다 작을 수 없습니다.") + Long ownerId, + + @Schema(description = "익명 댓글 여부", example = "false") + @NotNull(message = IS_ANONYMOUS_IS_NULL) + Boolean isAnonymous, + + @Schema(description = "댓글 내용", example = "우와 이 프로젝트 대박인데요?") + @Size(max = MAX_COMMENT_LENGTH, message = CONTENT_OVER_MAX_LENGTH) + @NotBlank(message = CONTENT_IS_NULL) + String content +) { +} diff --git a/src/main/java/sixgaezzang/sidepeek/comments/dto/response/CommentListResponse.java b/src/main/java/sixgaezzang/sidepeek/comments/dto/response/CommentListResponse.java new file mode 100644 index 00000000..e140c9c3 --- /dev/null +++ b/src/main/java/sixgaezzang/sidepeek/comments/dto/response/CommentListResponse.java @@ -0,0 +1,11 @@ +package sixgaezzang.sidepeek.comments.dto.response; + +import io.swagger.v3.oas.annotations.media.Schema; +import java.util.List; + +@Schema(description = "댓글 목록 응답 정보") +public record CommentListResponse( + @Schema(description = "댓글 목록 정보(오래된 순)") + List comments +) { +} diff --git a/src/main/java/sixgaezzang/sidepeek/comments/dto/response/CommentResponse.java b/src/main/java/sixgaezzang/sidepeek/comments/dto/response/CommentResponse.java new file mode 100644 index 00000000..61e29b8a --- /dev/null +++ b/src/main/java/sixgaezzang/sidepeek/comments/dto/response/CommentResponse.java @@ -0,0 +1,30 @@ +package sixgaezzang.sidepeek.comments.dto.response; + +import com.fasterxml.jackson.annotation.JsonFormat; +import io.swagger.v3.oas.annotations.media.Schema; +import java.time.LocalDateTime; +import sixgaezzang.sidepeek.users.dto.response.UserSummary; +// TODO: 대댓글 반영 부탁해용!! + +@Schema(description = "댓글 응답 정보") +public record CommentResponse( + @Schema(description = "댓글 식별자", example = "1") + Long id, + + @Schema(description = "댓글 작성자 정보(익명 댓글은 null)") + UserSummary owner, + + @Schema(description = "댓글 작성자와 로그인 사용자 일치 여부") + boolean isOwner, + + @Schema(description = "익명 댓글 여부") + boolean isAnonymous, + + @Schema(description = "댓글 내용", example = "우와 이 프로젝트 대박인데요?") + String content, + + @Schema(description = "댓글 생성 시각", example = "2024-03-02 20:17:00", type = "string") + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") + LocalDateTime createdAt +) { +} diff --git a/src/main/java/sixgaezzang/sidepeek/comments/exception/message/CommentErrorMessage.java b/src/main/java/sixgaezzang/sidepeek/comments/exception/message/CommentErrorMessage.java new file mode 100644 index 00000000..0ad04f60 --- /dev/null +++ b/src/main/java/sixgaezzang/sidepeek/comments/exception/message/CommentErrorMessage.java @@ -0,0 +1,12 @@ +package sixgaezzang.sidepeek.comments.exception.message; + +import static sixgaezzang.sidepeek.comments.util.CommentConstant.MAX_COMMENT_LENGTH; + +public class CommentErrorMessage { + // isAnonymous + public static final String IS_ANONYMOUS_IS_NULL = "익명 댓글 여부를 입력해주세요."; + + // content + public static final String CONTENT_IS_NULL = "댓글 내용을 입력해주세요."; + public static final String CONTENT_OVER_MAX_LENGTH = "댓글은 " + MAX_COMMENT_LENGTH + "자 이하여야 합니다."; +} diff --git a/src/main/java/sixgaezzang/sidepeek/comments/util/CommentConstant.java b/src/main/java/sixgaezzang/sidepeek/comments/util/CommentConstant.java new file mode 100644 index 00000000..fe5a728e --- /dev/null +++ b/src/main/java/sixgaezzang/sidepeek/comments/util/CommentConstant.java @@ -0,0 +1,5 @@ +package sixgaezzang.sidepeek.comments.util; + +public class CommentConstant { + public static final int MAX_COMMENT_LENGTH = 300; +} diff --git a/src/main/java/sixgaezzang/sidepeek/projects/service/ProjectSkillService.java b/src/main/java/sixgaezzang/sidepeek/projects/service/ProjectSkillService.java index 0722c445..b9644645 100644 --- a/src/main/java/sixgaezzang/sidepeek/projects/service/ProjectSkillService.java +++ b/src/main/java/sixgaezzang/sidepeek/projects/service/ProjectSkillService.java @@ -48,8 +48,7 @@ public List saveAll(Project project, List findAll(Project project) { return projectSkillRepository.findAllByProject(project); } diff --git a/src/main/java/sixgaezzang/sidepeek/projects/util/validation/ProjectValidator.java b/src/main/java/sixgaezzang/sidepeek/projects/util/validation/ProjectValidator.java index 41a0553d..6d571730 100644 --- a/src/main/java/sixgaezzang/sidepeek/projects/util/validation/ProjectValidator.java +++ b/src/main/java/sixgaezzang/sidepeek/projects/util/validation/ProjectValidator.java @@ -60,7 +60,6 @@ public static void validateDescription(String description) { validateTextLength(description, DESCRIPTION_OVER_MAX_LENGTH); } - public static void validateOwnerId(Long ownerId) { Assert.notNull(ownerId, OWNER_ID_IS_NULL); } diff --git a/src/test/java/sixgaezzang/sidepeek/projects/service/FileServiceTest.java b/src/test/java/sixgaezzang/sidepeek/projects/service/FileServiceTest.java index d7f06dd7..0b44e99f 100644 --- a/src/test/java/sixgaezzang/sidepeek/projects/service/FileServiceTest.java +++ b/src/test/java/sixgaezzang/sidepeek/projects/service/FileServiceTest.java @@ -1,6 +1,5 @@ package sixgaezzang.sidepeek.projects.service; - import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.AssertionsForClassTypes.assertThatExceptionOfType; import static sixgaezzang.sidepeek.projects.exception.message.FileErrorMessage.OVERVIEW_IMAGE_OVER_MAX_COUNT; @@ -84,7 +83,6 @@ void setup() { assertThat(savedImageUrls).isNull(); } - @Test void 목록_개수가_최대를_넘어서_파일_목록_저장에_실패한다() { // given, when diff --git a/src/test/java/sixgaezzang/sidepeek/projects/service/ProjectSkillServiceTest.java b/src/test/java/sixgaezzang/sidepeek/projects/service/ProjectSkillServiceTest.java index 3a3a0a40..85017a00 100644 --- a/src/test/java/sixgaezzang/sidepeek/projects/service/ProjectSkillServiceTest.java +++ b/src/test/java/sixgaezzang/sidepeek/projects/service/ProjectSkillServiceTest.java @@ -43,12 +43,16 @@ class ProjectSkillServiceTest { @Autowired ProjectSkillService projectSkillService; + @Autowired ProjectSkillRepository projectSkillRepository; + @Autowired SkillRepository skillRepository; + @Autowired ProjectRepository projectRepository; + @Autowired UserRepository userRepository;